web-dev-qa-db-ja.com

条件付きコンパイルとフレームワークターゲット

ターゲットフレームワークが新しいバージョンである場合、私のプロジェクトのコードを大幅に改善できるマイナーな場所がいくつかあります。必要に応じてこれらを切り替えるために、C#の条件付きコンパイルをより活用できるようにしたいと思います。

何かのようなもの:

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

これらの記号は無料で提供されますか?プロジェクト構成の一部としてこれらのシンボルを挿入する必要がありますか?どのフレームワークがMSBuildからターゲットにされているかがわかるので、簡単に実行できるようです。

/p:DefineConstants="NET40"

更新:私の質問は、人々がこの状況をどのように扱っているかです。別の構成を作成していますか?コマンドライン経由で定数を渡しますか?

119
mckamey

これを実現する最良の方法の1つは、プロジェクトに異なるビルド構成を作成することです。

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

これまでのところ私のために働いている代替案は、プロジェクトファイルに以下を追加することです:

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

これは、「v3.5」のようなTargetFrameworkVersionプロパティの値を取り、「v」と「。」を置き換えます。 「NET35」を取得します(新しい Property Functions 機能を使用)。次に、既存の「NETxx」値を削除し、DefinedConstantsの最後に追加します。これを合理化することは可能かもしれませんが、いじる時間はありません。

VSのプロジェクトプロパティの[ビルド]タブを見ると、条件付きコンパイルシンボルセクションに結果の値が表示されます。 [アプリケーション]タブでターゲットフレームワークのバージョンを変更すると、シンボルが自動的に変更されます。その後、通常の方法で#if NETxxプリプロセッサディレクティブを使用できます。 VSでプロジェクトを変更しても、カスタムPropertyGroupは失われません。

これは、クライアントプロファイルのターゲットオプションに対して異なるものを提供するようには見えないことに注意してください。しかし、それは私にとっては問題ではありません。

43
Jeremy Cook

これらのソリューションには問題がありました。おそらく、初期定数がこれらのプロパティによって事前に構築されていたためです。

<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

15
Nathaniel Roark

定数をクリアすることから始めます。

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

このアプローチは非常に読みやすく、理解しやすいと思います。

4
zDougie

@Azarien、あなたの答えをジェレミーのものと組み合わせて、Debug | Releaseなどではなく1か所に保管することができます。

私にとって、両方のバリエーションを組み合わせるのが最適です。つまり、#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
ghanashyaml

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

デバッグとリリースの両方のビルド構成に対してこれを行います。次に、コードで使用します。

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