J'ai du code qui utilise des méthodes d'extension, mais compile sous .NET 2.0 en utilisant le compilateur dans VS2008. Pour faciliter cela, j'ai dû déclarer ExtensionAttribute:
/// <summary>
/// ExtensionAttribute is required to define extension methods under .NET 2.0
/// </summary>
public sealed class ExtensionAttribute : Attribute
{
}
Cependant, j'aimerais maintenant que la bibliothèque dans laquelle cette classe est contenue soit également compilable sous .NET 3.0, 3.5 et 4.0 - sans l'avertissement "ExtensionAttribute est défini à plusieurs endroits".
Existe-t-il une directive de temps de compilation que je peux utiliser pour inclure uniquement ExtensionAttribute lorsque la version du framework ciblée est .NET 2?
La question liée SO avec "créer N configurations différentes" est certainement une option, mais quand j'en avais besoin, je viens d'ajouter des éléments conditionnels DefineConstants, donc dans mon Debug | x86 (par exemple) après les DefineConstants existants pour DEBUG; TRACE, j'ai ajouté ces 2, en vérifiant la valeur dans TFV qui a été définie dans le premier PropertyGroup du fichier csproj.
<DefineConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">RUNNING_ON_4</DefineConstants>
<DefineConstants Condition=" '$(TargetFrameworkVersion)' != 'v4.0' ">NOT_RUNNING_ON_4</DefineConstants>
Vous n'avez pas besoin des deux, évidemment, mais il est juste là pour donner des exemples de comportement à la fois eq et ne - #else et #Elif fonctionnent bien aussi :)
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
}
}
Je pourrais alors basculer entre le ciblage 3.5 et 4.0 et cela ferait la bonne chose.
J'ai quelques suggestions pour améliorer les réponses données jusqu'à présent:
Utilisez Version.CompareTo (). Le test d'égalité ne fonctionnera pas pour les versions ultérieures du framework, mais n'a pas encore été nommé. Par exemple.
<CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">
ne correspondra pas à v4.5 ou v4.5.1, ce que vous voulez généralement.
Utilisez un fichier d'importation pour que ces propriétés supplémentaires ne soient définies qu'une seule fois. Je recommande de garder le fichier d'import sous contrôle de source, afin que les modifications soient propagées avec les fichiers du projet, sans effort supplémentaire.
Ajoutez l'élément d'importation à la fin de votre fichier de projet, afin qu'il soit indépendant de tout groupe de propriétés spécifique à la configuration. Cela présente également l'avantage d'exiger une seule ligne supplémentaire dans votre fichier de projet.
<!--
******************************************************************
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>
Référencez-le à partir de votre fichier .csproj en ajoutant à la fin, avant la balise.
…
<Import Project="VersionSpecificSymbols.Common.prop" />
</Project>
Vous devrez corriger le chemin pour pointer vers le dossier commun/partagé où vous placez ce fichier.
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();
}
}
}
Implémentation de Join (délimiteur de chaînes, chaînes IEnumerable) avant .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
Évaluation de la propriété MSBuild
Puis-je rendre une directive de préprocesseur dépendante de la version du framework .NET?
Compilation conditionnelle selon la version du framework en C #
Les groupes de propriétés sont écrasés uniquement, ce qui entraînerait la suppression de vos paramètres pour DEBUG
, TRACE
ou tout autre. - Voir Évaluation de la propriété MSBuild
De plus, si la propriété DefineConstants
est définie à partir de la ligne de commande, tout ce que vous lui faites dans le fichier de projet est sans importance car ce paramètre devient global en lecture seule. Cela signifie que vos modifications apportées à cette valeur échouent silencieusement.
Exemple de maintien des constantes définies existantes:
<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>
Cette section DOIT venir après toutes les autres constantes définies car il est peu probable qu'elles soient configurées de manière additive
J'ai seulement défini ces 2 parce que c'est surtout ce qui m'intéresse dans mon projet, ymmv.
Voir aussi: Propriétés communes du projet MsBuild
Les symboles prédéfinis pour les frameworks cibles sont désormais intégrés dans la version de MSBuild qui est utilisée par l'outil dotnet
et par VS 2017 et les versions ultérieures. Voir https://docs.Microsoft.com/en-us/dotnet/standard/frameworks#how-to-specify-target-frameworks pour la liste complète.
#if NET47
Console.WriteLine("Running on .Net 4.7");
#Elif NETCOREAPP2_0
Console.WriteLine("Running on .Net Core 2.0");
#endif
Je voudrais apporter une réponse mise à jour qui résout certains problèmes.
Si vous définissez DefineConstants au lieu de CustomConstants, vous vous retrouverez, dans la ligne de commande Débogage des symboles de compilation conditionnelle, après un changement de version de framework, avec des constantes conditionnelles dupliquées (par exemple: NETFX_451; NETFX_45; NETFX_40; NETFX_35; NETFX_30; NETFX_20; NETFX_35; NETFX_30 ; NETFX_20;). Il s'agit du VersionSpecificSymbols.Common.prop qui résout tout problème.
<!--
*********************************************************************
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>
Utilisez la réflexion pour déterminer si la classe existe. Si c'est le cas, alors dynamiquement créez et utilisez-le, sinon utilisez la classe de contournement .Net2 qui peut être définie, mais pas utilisée pour toutes les autres versions .net.
Voici le code que j'ai utilisé pour un AggregateException
qui est .Net 4 et supérieur uniquement:
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.