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


124

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

何かのようなもの:

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

これらのシンボルのいずれかが無料で提供されますか?これらのシンボルをプロジェクト構成の一部として挿入する必要がありますか?どのフレームワークがMSBuildのターゲットになっているのかを知ることができるので、それは十分簡単に​​思えます。

/p:DefineConstants="NET40"

人々はこの状況をどのように扱っていますか?異なる構成を作成していますか?コマンドラインから定数を渡していますか?



VSで簡単なプリベークソリューションが必要な場合は、このユーザーの声、visualstudio.uservoice.com / forums / 121579 -visual-studio /…に賛成票を投じてください。
JohnC

1
このリンクもご覧ください。かなり説明的。 blogs.msmvps.com/punitganshani/2015/06/21/...
マルコ・アルベス

プロジェクトグループは、復元、およびnuget REFグループ、素敵なソリューションnuget:shazwazza.com/post/...
OzBob

回答:


119

これを実現する最良の方法の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>

そして、あなたのデフォルト設定の1つで:

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

完璧です。msbuildフォーマットをハッキングして、それが実行できることを知るには十分な経験がありましたが、すべての詳細を理解するのに十分な時間はありませんでした。どうもありがとうございました!
mckamey

関連する質問(stackoverflow.com/questions/2923181)にこの回答への参照を追加すると、解決策としてマークされます。これは実際には両方を同時に解決します。
mckamey

7
回答ありがとうございます。ただし、VS2010にはすでに "TargetFrameworkVersion"という名前の新しいタグが含まれています。条件付きのプロパティグループごとに、TargetFrameworkVersionのみが変更されています。これを機能させるために、これらすべてにまだ必要ですか?
Akash Kava 2010

この答えは、フレームワークの定数を定義することだけでなく、複数のフレームワークのために構築することでもあります
katbyte

4
この投稿はうまくいきましたが、MSBuildは苦手で、理解するのにしばらく時間がかかりました。例として機能するプロジェクトを作成しました。dev6.blob.core.windows.net/blog-images/DualTargetFrameworks.zip
TheDev6

44

これまでのところ機能している別の方法は、プロジェクトファイルに以下を追加することです。

 <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」を取得するには(新しいプロパティ関数機能を使用)。次に、既存の「NETxx」値を削除し、DefinedConstantsの末尾に追加します。これを合理化することは可能かもしれませんが、私はいじくる時間がありません。

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

これはクライアントプロファイルターゲットオプションに何か違いを与えるようには見えませんが、それは私にとって問題ではないことに注意してください。


ジェレミー、私はすでにビルドソリューションで個別にビルドしているので、これは完璧です。
Greg Finzer

+1。「$(DefineConstants.Contains( '...」)を見つけるのがとても難しいと思った人はいますか??ありがとう
CADが

これらの魔法の定数をビルドに組み込む方法について復習が必要だったので、ようやくこのページに戻る方法が見つかりました。私は今日、同じプロジェクトを再検討して、ライブラリを細分します。シンボルをいくつかの細分に一緒に入れる必要があります。私はその上を見たところ、元の.CSPROJファイルであなたの答えがすでに正式に認められていることに気づきました。
デビッドA.グレイ

15

私の初期定数がこれらのプロパティによって事前に作成されたためか、これらのソリューションに問題がありました。

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

(プロジェクトの[コンパイル]タブの[詳細コンパイルオプション...]ボタンをクリックして開かれる)[詳細コンパイラ設定]ダイアログのスクリーンショットを投稿します。しかし、新しいユーザーとして、私にはそうするための担当者がいません。スクリーンショットを見ることができれば、プロパティグループによって自動入力されるカスタム定数が表示され、「私はそれの一部を取得しなければならない」と言うでしょう。


編集:その担当者が驚くほど高速になりました。ありがとう。これがそのスクリーンショットです。

高度なコンパイラ設定


4

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

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

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


3

.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

3
デフォルトパラメータと名前付きパラメータは、.NET Framework 4の機能ではなく、.NET 4コンパイラの機能です。Visual Studio 2010でコンパイルされている限り、.NET 2または.NET 3をターゲットとするプロジェクトでも使用できます。これは単なる構文上の砂糖です。一方、動的は.NET Framework 4の機能であり、これより前のフレームワークをターゲットとするプロジェクトでは使用できません。
Thanasis Ioannidis

2

@Azarien、あなたの答えをJeremyの答えと組み合わせて、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>

0

.NET Coreビルドシステムを使用している場合は、事前定義されたシンボルを使用できます(実際には既にサンプルに一致しており、.csproj!を変更する必要はありません)。

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

事前定義されたシンボルのリストは、クロスプラットフォームツールを使用したライブラリの開発#if(C#リファレンス)に記載されています。

.NET Frameworkの: NETFRAMEWORKNET20NET35NET40NET45NET451NET452NET46NET461NET462NET47NET471NET472NET48

.NET標準: NETSTANDARDNETSTANDARD1_0NETSTANDARD1_1NETSTANDARD1_2NETSTANDARD1_3NETSTANDARD1_4NETSTANDARD1_5NETSTANDARD1_6NETSTANDARD2_0NETSTANDARD2_1

.NETのコア: NETCOREAPPNETCOREAPP1_0NETCOREAPP1_1NETCOREAPP2_0NETCOREAPP2_1NETCOREAPP2_2NETCOREAPP3_0

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.