Определить целевую версию фреймворка во время компиляции - PullRequest
56 голосов
/ 09 августа 2010

У меня есть некоторый код, который использует методы расширения, но компилируется в .NET 2.0 с использованием компилятора в VS2008.Чтобы облегчить это, я должен был объявить ExtensionAttribute:

/// <summary>
/// ExtensionAttribute is required to define extension methods under .NET 2.0
/// </summary>
public sealed class ExtensionAttribute : Attribute
{
}

Однако теперь я хотел бы, чтобы библиотека, в которой содержится этот класс, также могла быть скомпилирована в .NET 3.0, 3.5 и 4.0 - без 'ExtensionAttribute определен в предупреждении нескольких мест.

Существует ли какая-либо директива времени компиляции, которую я могу использовать, чтобы включить ExtensionAttribute, только если целевая версия платформы - .NET 2?

Ответы [ 6 ]

60 голосов
/ 09 августа 2010

Связанный вопрос SO с «создать N различных конфигураций», безусловно, является одним из вариантов, но когда мне это понадобилось, я просто добавил условные элементы DefineConstants, поэтому в моем Debug | x86 (например) после существующих DefineConstants для DEBUG; TRACE, я добавил эти 2, проверяя значение в TFV, которое было установлено в первой PropertyGroup файла csproj.

<DefineConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">RUNNING_ON_4</DefineConstants>
<DefineConstants Condition=" '$(TargetFrameworkVersion)' != 'v4.0' ">NOT_RUNNING_ON_4</DefineConstants>

Вам не нужны оба, очевидно, но это просто, чтобы привести примерыповедения eq и ne - #else и #elif тоже отлично работают:)

class Program
{
    static void Main(string[] args)
    {
#if RUNNING_ON_4
        Console.WriteLine("RUNNING_ON_4 was set");
#endif
#if NOT_RUNNING_ON_4
        Console.WriteLine("NOT_RUNNING_ON_4 was set");
#endif
    }
}

Затем я могу переключаться между таргетингом 3,5 и 4,0, и это будет правильно.

33 голосов
/ 12 марта 2015

У меня есть несколько предложений по улучшению ответов, данных на данный момент:

  1. Использовать Version.CompareTo ().Тестирование на равенство не будет работать для более поздних версий фреймворка, которые пока не названы.Например,

    <CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">
    

    не будет соответствовать v4.5 или v4.5.1, что обычно требуется.

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

  3. Добавьте элемент импорта в конец файла проекта, чтобычто он не зависит от конфигурационных групп свойств.Это также имеет то преимущество, что в файле проекта требуется одна дополнительная строка.

Вот файл импорта (VersionSpecificSymbols.Common.prop)

<!--
******************************************************************
Defines the Compile time symbols Microsoft forgot
Modelled from https://msdn.microsoft.com/en-us/library/ms171464.aspx
*********************************************************************
-->

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5.1')))) &gt;= 0">$(DefineConstants);NETFX_451</DefineConstants>
        <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5'))))   &gt;= 0">$(DefineConstants);NETFX_45</DefineConstants>
        <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.0'))))   &gt;= 0">$(DefineConstants);NETFX_40</DefineConstants>
        <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.5'))))   &gt;= 0">$(DefineConstants);NETFX_35</DefineConstants>
        <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.0'))))   &gt;= 0">$(DefineConstants);NETFX_30</DefineConstants>
    </PropertyGroup>
</Project>

ДобавитьИмпорт элемента в файл проекта

Ссылка на него из файла .csproj путем добавления в конце перед тегом.

…
    <Import Project="VersionSpecificSymbols.Common.prop" />
</Project>

Вам необходимо исправить путь, чтобы указать на общийПапка / shared, в которую вы помещаете этот файл.

Использование символов времени компиляции

namespace VersionSpecificCodeHowTo
{
    using System;

    internal class Program
    {
        private static void Main(string[] args)
        {
#if NETFX_451
            Console.WriteLine("NET_451 was set");
#endif

#if NETFX_45
            Console.WriteLine("NET_45 was set");
#endif

#if NETFX_40
            Console.WriteLine("NET_40 was set");
#endif

#if NETFX_35
            Console.WriteLine("NETFX_35 was set");
#endif

#if NETFX_30
            Console.WriteLine("NETFX_30 was set");
#endif

#if NETFX_20
             Console.WriteLine("NETFX_20 was set");
#else
           The Version specific symbols were not set correctly!
#endif

#if DEBUG
            Console.WriteLine("DEBUG was set");
#endif

#if MySymbol
            Console.WriteLine("MySymbol was set");
#endif
            Console.ReadKey();
        }
    }
}

Общий пример из реальной жизни

Реализация соединения (разделитель строк, строки IEnumerable) До .NET 4.0

// string Join(this IEnumerable<string> strings, string delimiter)
// was not introduced until 4.0. So provide our own.
#if ! NETFX_40 && NETFX_35
public static string Join( string delimiter, IEnumerable<string> strings)
{
    return string.Join(delimiter, strings.ToArray());
}
#endif

Ссылки

Функции свойств

Оценка свойства MSBuild

Можно ли сделать директиву препроцессора зависимой от версии платформы .NET?

Условная компиляция в зависимости от версии платформы в C #

29 голосов
/ 10 февраля 2011

Группы свойств перезаписываются только так, что это выбило бы ваши настройки для DEBUG, TRACE или любых других. - См. Оценка имущества MSBuild

Также, если свойство DefineConstants установлено из командной строки, все, что вы делаете с ним в файле проекта, не имеет значения, так как этот параметр становится глобальным только для чтения. Это означает, что ваши изменения этого значения не выполняются автоматически.

Пример ведения существующих определенных констант:

    <CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v2.0' ">V2</CustomConstants>
    <CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">V4</CustomConstants>
    <DefineConstants Condition=" '$(DefineConstants)' != '' And '$(CustomConstants)' != '' ">$(DefineConstants);</DefineConstants>
    <DefineConstants>$(DefineConstants)$(CustomConstants)</DefineConstants>

Этот раздел ДОЛЖЕН следовать после любых других определенных констант, поскольку вряд ли они будут установлены аддитивным образом

Я определил только эти 2, потому что это в основном то, что меня интересует в моем проекте, мммм.

См. Также: Общие свойства проекта MsBuild

5 голосов
/ 18 февраля 2018

Предопределенные символы для целевых структур теперь встроены в версию MSBuild, которая используется инструментом dotnet и VS 2017 года.См. https://docs.microsoft.com/en-us/dotnet/standard/frameworks#how-to-specify-target-frameworks для полного списка.

#if NET47
Console.WriteLine("Running on .Net 4.7");
#elif NETCOREAPP2_0
Console.WriteLine("Running on .Net Core 2.0");
#endif
5 голосов
/ 19 июля 2016

Я хотел бы внести свой вклад с обновленным ответом, который решает некоторые проблемы.

Если вы зададите DefineConstants вместо CustomConstants, то в конечном итоге вы попадете в командную строку «Отладка условных символов компиляции» после некоторого переключения версии фреймворка с дублированными условными константами (например: NETFX_451; NETFX_45; NETFX_40; NETFX_35; NETFX_30; NETFX_20; NETFX_35; NETFX_30; NETFX_20;). Это VersionSpecificSymbols.Common.prop, который решает любую проблему.

<!--
*********************************************************************
Defines the Compile time symbols Microsoft forgot
Modelled from https://msdn.microsoft.com/en-us/library/ms171464.aspx
*********************************************************************
Author: Lorenzo Ruggeri (lrnz.ruggeri@gmail.com)
-->

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Choose>
    <When Condition=" $(TargetFrameworkVersion) == 'v2.0' ">
      <PropertyGroup>
        <CustomConstants >$(CustomConstants);NETFX_20</CustomConstants>
      </PropertyGroup>
    </When>
    <When Condition=" $(TargetFrameworkVersion) == 'v3.0' ">
      <PropertyGroup>
        <CustomConstants >$(CustomConstants);NETFX_30</CustomConstants>
        <CustomConstants >$(CustomConstants);NETFX_20</CustomConstants>
      </PropertyGroup>
    </When>
    <When Condition=" $(TargetFrameworkVersion) == 'v3.5' ">
      <PropertyGroup>
        <CustomConstants >$(CustomConstants);NETFX_35</CustomConstants>
        <CustomConstants >$(CustomConstants);NETFX_30</CustomConstants>
        <CustomConstants >$(CustomConstants);NETFX_20</CustomConstants>
      </PropertyGroup>
    </When>
    <Otherwise>
      <PropertyGroup>
        <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5.1')))) &gt;= 0">$(CustomConstants);NETFX_451</CustomConstants>
        <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5')))) &gt;= 0">$(CustomConstants);NETFX_45</CustomConstants>
        <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.0')))) &gt;= 0">$(CustomConstants);NETFX_40</CustomConstants>
        <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.5')))) &gt;= 0">$(CustomConstants);NETFX_35</CustomConstants>
        <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.0')))) &gt;= 0">$(CustomConstants);NETFX_30</CustomConstants>
        <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('2.0')))) &gt;= 0">$(CustomConstants);NETFX_20</CustomConstants>
      </PropertyGroup>
    </Otherwise>
  </Choose>
  <PropertyGroup>
    <DefineConstants>$(DefineConstants);$(CustomConstants)</DefineConstants>
  </PropertyGroup>
</Project>
1 голос
/ 02 марта 2017

Используйте отражение, чтобы определить, существует ли класс. Если это так, тогда динамически создайте и используйте его, в противном случае используйте класс обходного пути .Net2, который можно определить, но не использовать для всех других версий .net.

Вот код, который я использовал для AggregateException, который является только .Net 4 и выше:

var aggregatException = Type.GetType("System.AggregateException");

if (aggregatException != null) // .Net 4 or greater
{
    throw ((Exception)Activator.CreateInstance(aggregatException, ps.Streams.Error.Select(err => err.Exception)));
}

// Else all other non .Net 4 or less versions
throw ps.Streams.Error.FirstOrDefault()?.Exception 
      ?? new Exception("Powershell Exception Encountered."); // Sanity check operation, should not hit.
...