Compilation conditionnelle et objectifs du cadre

Il y a quelques endroits mineurs où le code de mon projet peut être considérablement amélioré si le framework cible était une version plus récente. Je voudrais être en mesure de mieux tirer parti de la compilation conditionnelle en C# pour les changer au besoin.

Quelque Chose comme:

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

Est-ce que l'un de ces symboles est gratuit? Dois-je injecter ces symboles dans le cadre de la configuration du projet? Semble assez facile à faire puisque je saurai de quel framework est ciblé MSBuild.

/p:DefineConstants="NET40"

Mise à jour: {[10] } ma question Est de savoir comment les gens gèrent-ils cette situation? Créez-vous différentes configurations? Passez-vous les constantes via la ligne de commande?

117
demandé sur abatishchev 2010-05-27 21:05:43

6 réponses

L'une des meilleures façons d'y parvenir est de créer différentes configurations de construction dans votre projet:

<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>

Et dans l'une de vos configurations par défaut:

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

Qui définirait la valeur par défaut si elle n'était définie nulle part ailleurs. Dans le cas ci-dessus, le chemin de sortie vous donnera un assemblage séparé chaque fois que vous construisez chaque version.

Créez ensuite une cible AfterBuild pour compiler vos différentes versions:

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

Cet exemple recompile l'ensemble du projet avec la variable Framework définie sur NET20 après la première construction (en compilant les deux et en supposant que la première construction était la valeur par défaut NET35 d'en haut). Chaque Compilation aura les valeurs de définition conditionnelles définies correctement.

De cette manière, vous pouvez même exclure certains fichiers dans le fichier de projet si vous voulez sans avoir à # ifdef les fichiers:

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

Ou même des références

<Reference Include="Some.Assembly" Condition="" '$(Framework)' == 'NET20' " >
  <HintPath>..\Lib\$(Framework)\Some.Assembly.dll</HintPath>
</Reference>
114
répondu Todd 2010-05-28 12:15:18

Une alternative qui fonctionne pour moi jusqu'à présent est d'ajouter ce qui suit au fichier de projet:

 <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>

Cela prend la valeur de la propriété TargetFrameworkVersion, qui est comme "v3.5", remplace le "v" et"."pour obtenir "NET35" (en utilisant la nouvelle fonction Property Functions). Il supprime ensuite toute valeur "NETxx" existante et l'ajoute à la fin des DefinedConstants. Il est peut-être possible de rationaliser cela, mais je n'ai pas le temps de jouer.

En regardant sur L'onglet Build du propriétés du projet dans VS vous verrez la valeur résultante dans la section symboles de compilation conditionnelle. La modification de la version du cadre cible dans L'onglet Application modifie ensuite automatiquement le symbole. Vous pouvez ensuite utiliser les directives du préprocesseur #if NETxx de la manière habituelle. Changer le projet dans VS ne semble pas perdre le PropertyGroup personnalisé.

Notez que cela ne semble pas vous donner quelque chose de différent pour les options de cible du profil Client, mais ce n'est pas un problème pour moi.

41
répondu Jeremy Cook 2010-12-16 04:44:41

J'ai eu des problèmes avec ces solutions, peut-être parce que mes constantes initiales étaient pré-construites par ces propriétés.

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

Visual Studio 2010 a également émis une erreur à cause des points-virgules, affirmant qu'ils sont des caractères illégaux. Le message d'erreur m'a donné un indice car je pouvais voir les constantes pré-construites séparées par des virgules, éventuellement suivies de mon point-virgule "illégal". Après un reformatage et un massage, j'ai pu trouver une solution qui fonctionne pour je.

<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>

Je posterais une capture d'écran de la boîte de dialogue Paramètres avancés du compilateur (ouverte en cliquant sur "Options de compilation avancées..."sur l'onglet Compiler de votre projet). Mais en tant que nouvel utilisateur, je manque le représentant pour le faire. Si vous pouviez voir la capture d'écran, vous verriez les constantes personnalisées remplies automatiquement par le groupe de propriétés et vous diriez: "je dois m'en procurer un peu."


EDIT: a obtenu ce représentant étonnamment rapide.. Merci les gars! Voici que capture d'écran:

Paramètres Avancés Du Compilateur

12
répondu Nathaniel Roark 2016-08-18 12:33:18

Commencez par Effacer les constantes:

<PropertyGroup>
  <DefineConstants/>
</PropertyGroup>

Ensuite, construisez votre debug, trace et d'autres constantes comme:

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

Enfin, construisez vos constantes de framework:

<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>

Je pense que cette approche est très lisible et compréhensible.

3
répondu zDougie 2016-01-06 10:48:30

Dans A.fichier csproj, après une ligne <DefineConstants>DEBUG;TRACE</DefineConstants> existante, ajoutez ceci:

<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>

Faites ceci pour les configurations de build Debug et Release. Ensuite, utilisez dans votre code:

#if NET_40_OR_GREATER
   // can use dynamic, default and named parameters
#endif
2
répondu Azarien 2011-10-25 11:29:40

@ Azarien, votre réponse peut être combinée avec celle de Jeremy pour la garder à un endroit plutôt que de déboguer|libérer etc.

Pour moi, combiner les deux variations fonctionne mieux, c'est-à-dire inclure des conditions dans le code en utilisant #if NETXX et aussi construire pour différentes versions de framework en une seule fois.

J'ai ceux-ci dans mon .fichier 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>

Et dans les cibles:

  <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
répondu ghanashyaml 2011-11-02 18:33:09