Обнаружение целевой версии платформы во время компиляции
У меня есть код, который использует методы расширения, но компилируется под .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, когда в рамках версии рассчитана на это .Чистая 2?
6 ответов:
связанный вопрос SO с "создать N разных конфигураций", безусловно, является одним из вариантов, но когда у меня возникла необходимость в этом, я просто добавил условные элементы DefineConstants, поэтому в моем Debug|x86 (например) после существующих DefineConstants для отладки;трассировка, я добавил Эти 2, проверяя значение в TFV, которое было установлено в первой группе свойств файла 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, и это было бы правильно.
группы свойств перезаписываются только так, чтобы это выбило ваши настройки для
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, потому что это в основном то, что меня интересует в моем проекте, ymmv.
Читайте Также: Общие Свойства Проекта MsBuild
у меня есть несколько предложений по улучшению на ответы до сих пор:
Используйте Версию.CompareTo (). Тестирование на равенство не будет работать для более поздних версий фреймворка, пока не будет названо. Е. Г.
<CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">не будет соответствовать v4.5 или v4.5.1, которые обычно вы хотите.
используйте файл импорта, чтобы эти дополнительные свойства нужно было определить только один раз. Я рекомендую сохранить файл импорта под управлением исходного кода, чтобы изменения распространяются вместе с файлами проекта, без дополнительных усилий.
добавьте элемент импорта в конце файла проекта, чтобы он не зависел от каких-либо групп свойств конфигурации. Это также имеет то преимущество, что требуется одна дополнительная строка в файле проекта.
вот файл импорта (VersionSpecificSymbols.Общий.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')))) >= 0">$(DefineConstants);NETFX_451</DefineConstants> <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5')))) >= 0">$(DefineConstants);NETFX_45</DefineConstants> <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.0')))) >= 0">$(DefineConstants);NETFX_40</DefineConstants> <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.5')))) >= 0">$(DefineConstants);NETFX_35</DefineConstants> <DefineConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.0')))) >= 0">$(DefineConstants);NETFX_30</DefineConstants> </PropertyGroup> </Project>добавить элемент импорта в проект Файл
ссылка на него из вашего .csproj файл, добавив в конце, перед тегом.
… <Import Project="VersionSpecificSymbols.Common.prop" /> </Project>вам нужно будет исправить путь, чтобы указать на общую / общую папку, в которую вы поместили этот файл.
Использовать Символы Времени Компиляции
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(); } } }Общий Пример "Реальной Жизни"
реализация Join (string delimiter, IEnumerable strings) до .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ссылки
можно ли сделать директиву препроцессора зависимой от версии .NET framework?
Я хотел бы внести свой вклад в обновленный ответ, который решает некоторые вопросы.
Если вы установите DefineConstants вместо CustomConstants вы будете в конечном итоге, в символы условной компиляции, отладки командной строки, через некоторое Framework версии переключатель, с дублируются условной константы (т. е.: NETFX_451;NETFX_45;NETFX_40;NETFX_35;NETFX_30;NETFX_20;NETFX_35;NETFX_30;NETFX_20;). Это версии Specificsymbols.Общий.опора, которая решает любую проблему.
<!-- ********************************************************************* Defines the Compile time symbols Microsoft forgot Modelled from https://msdn.microsoft.com/en-us/library/ms171464.aspx ********************************************************************* Author: Lorenzo Ruggeri ([email protected]) --> <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')))) >= 0">$(CustomConstants);NETFX_451</CustomConstants> <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.5')))) >= 0">$(CustomConstants);NETFX_45</CustomConstants> <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('4.0')))) >= 0">$(CustomConstants);NETFX_40</CustomConstants> <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.5')))) >= 0">$(CustomConstants);NETFX_35</CustomConstants> <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('3.0')))) >= 0">$(CustomConstants);NETFX_30</CustomConstants> <CustomConstants Condition="$([System.Version]::Parse('$(TargetFrameworkVersion.Substring(1))').CompareTo($([System.Version]::Parse('2.0')))) >= 0">$(CustomConstants);NETFX_20</CustomConstants> </PropertyGroup> </Otherwise> </Choose> <PropertyGroup> <DefineConstants>$(DefineConstants);$(CustomConstants)</DefineConstants> </PropertyGroup> </Project>
предопределенные символы для целевых фреймворков теперь встроены в версию MSBuild, которая используется
dotnetинструмент и по сравнению с 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
используйте отражение, чтобы определить, существует ли класс. Если это так, то динамически создайте и используйте его, в противном случае используйте класс обхода .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.
Comments