webentwicklung-frage-antwort-db.com.de

Bedingte Kompilierung und Rahmenziele

Es gibt ein paar kleinere Stellen, an denen sich der Code für mein Projekt möglicherweise drastisch verbessern lässt, wenn das Zielframework eine neuere Version wäre. Ich möchte die bedingte Kompilierung in C # besser nutzen können, um diese nach Bedarf zu ändern.

So etwas wie:

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

Ist eines dieser Symbole kostenlos? Muss ich diese Symbole als Teil der Projektkonfiguration einfügen? Scheint einfach zu sein, da ich weiß, auf welches Framework MSBuild abzielt.

/p:DefineConstants="NET40"

pdate: Wie gehen die Leute mit dieser Situation um? Erstellen Sie verschiedene Konfigurationen? Übergeben Sie die Konstanten über die Befehlszeile?

119
mckamey

Eine der besten Möglichkeiten, dies zu erreichen, besteht darin, verschiedene Build-Konfigurationen in Ihrem Projekt zu erstellen:

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

Und in einer Ihrer Standardkonfigurationen:

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

Womit die Standardeinstellung festgelegt würde, wenn sie nirgendwo anders definiert wäre. In dem obigen Fall gibt Ihnen der OutputPath jedes Mal, wenn Sie eine Version erstellen, eine separate Assembly.

Erstellen Sie dann ein AfterBuild-Ziel, um Ihre verschiedenen Versionen zu kompilieren:

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

In diesem Beispiel wird das gesamte Projekt neu kompiliert, wobei die Framework-Variable nach dem ersten Build auf NET20 gesetzt wird (wobei beide kompiliert werden und davon ausgegangen wird, dass der erste Build das Standard-NET35 von oben war). Für jede Kompilierung werden die bedingten Definitionswerte korrekt festgelegt.

Auf diese Weise können Sie sogar bestimmte Dateien in der Projektdatei ausschließen, wenn Sie die Dateien nicht #ifdef müssen:

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

oder sogar Referenzen

<Reference Include="Some.Assembly" Condition="" '$(Framework)' == 'NET20' " >
  <HintPath>..\Lib\$(Framework)\Some.Assembly.dll</HintPath>
</Reference>
117
Todd

Eine Alternative, die bisher für mich funktioniert, besteht darin, der Projektdatei Folgendes hinzuzufügen:

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

Hierbei wird der Wert der TargetFrameworkVersion-Eigenschaft verwendet, der "v3.5" entspricht und "v" und "." Ersetzt. um "NET35" zu erhalten (mit der neuen Property Functions Funktion). Anschließend werden alle vorhandenen "NETxx" -Werte entfernt und an das Ende der DefinedConstants angefügt. Es mag möglich sein, dies zu rationalisieren, aber ich habe nicht die Zeit, um zu fummeln.

Wenn Sie in VS auf der Registerkarte Erstellen der Projekteigenschaften nachsehen, wird der resultierende Wert im Abschnitt Bedingte Kompilierungssymbole angezeigt. Wenn Sie die Ziel-Framework-Version auf der Registerkarte "Anwendung" ändern, wird das Symbol automatisch geändert. Sie können dann #if NETxx Präprozessor-Direktiven wie gewohnt. Das Ändern des Projekts in VS scheint die benutzerdefinierte PropertyGroup nicht zu verlieren.

Beachten Sie, dass dies für die Zieloptionen des Kundenprofils anscheinend nichts anderes ergibt, aber das ist für mich kein Problem.

43
Jeremy Cook

Ich hatte Probleme mit diesen Lösungen, möglicherweise, weil meine Anfangskonstanten durch diese Eigenschaften vorab erstellt wurden.

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

Visual Studio 2010 hat außerdem einen Fehler aufgrund der Semikolons gemeldet, die behaupten, es handele sich um ungültige Zeichen. Die Fehlermeldung gab mir einen Hinweis, da ich die vorgefertigten Konstanten sehen konnte, die durch Kommas getrennt waren, gefolgt von meinem "illegalen" Semikolon. Nach einigen Neuformatierungen und Massagen konnte ich eine Lösung finden, die für mich funktioniert.

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

Ich würde einen Screenshot des Dialogfelds "Erweiterte Compiler-Einstellungen" posten (geöffnet durch Klicken auf die Schaltfläche "Erweiterte Compiler-Optionen ..." auf der Registerkarte "Compile" Ihres Projekts). Aber als neuer Benutzer fehlt mir der Repräsentant, um dies zu tun. Wenn Sie den Screenshot sehen könnten, würden Sie die benutzerdefinierten Konstanten sehen, die von der Eigenschaftsgruppe automatisch ausgefüllt werden, und dann würden Sie sagen: "Ich muss mir etwas davon besorgen."


BEARBEITEN: Habe diesen Repräsentanten überraschend schnell. Danke Jungs! Hier ist der Screenshot:

Advanced Compiler Settings

15
Nathaniel Roark

Beginnen Sie mit dem Löschen der Konstanten:

<PropertyGroup>
  <DefineConstants/>
</PropertyGroup>

Als nächstes bauen Sie Ihre Debug-, Trace- und andere Konstanten wie:

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

Zuletzt erstellen Sie Ihre Framework-Konstanten:

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

Ich denke, dieser Ansatz ist sehr lesbar und verständlich.

4
zDougie

@Azarien, deine Antwort kann mit der von Jeremy kombiniert werden, um sie an einem Ort zu behalten, anstatt Debug | Release usw.

Für mich funktioniert es am besten, beide Varianten zu kombinieren, d. H. Bedingungen in Code mit #if NETXX einzubeziehen und gleichzeitig für verschiedene Framework-Versionen zu erstellen.

Ich habe diese in meiner .csproj-Datei:

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

und in Zielen:

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

In einer .csproj-Datei wird nach einem vorhandenen <DefineConstants>DEBUG;TRACE</DefineConstants> Zeile, füge dies hinzu:

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

Führen Sie dies sowohl für Debug- als auch für Release-Build-Konfigurationen aus. Dann verwenden Sie in Ihrem Code:

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