Détection de la version cible du framework au moment de la compilation
j'ai du code qui utilise des méthodes D'Extension, mais qui se 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, je voudrais maintenant que la bibliothèque dans laquelle cette classe est contenue soit aussi compilable sous .NET 3.0, 3.5 et 4.0 - sans 'ExtensionAttribute est défini dans l'avertissement de plusieurs endroits'.
Existe-t-il une directive de compilation que je ne peux utiliser inclure L'ExtensionAttribute lorsque la version de cadre visée est .NET 2?
6 réponses
la question liée SO avec 'create N different configurations' est certainement une option, mais quand j'en avais besoin j'ai juste ajouté 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 groupe de propriété 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 deux eq et ne de comportement - #else et #elif travail trop belle :)
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 passer du ciblage 3.5 au 4.0 et ça ferait la bonne chose.
les groupes de propriétés sont écrasés seulement de sorte que cela détruirait vos paramètres pour DEBUG
, TRACE
, ou tout autre. - Voir Évaluation De La Propriété De MSBuild
aussi si la propriété DefineConstants
est définie à partir de la ligne de commande, Tout ce que vous faites à l'intérieur du fichier du projet n'est pas pertinent car ce paramètre devient global en lecture seule. Cela signifie que vos modifications à cette valeur échouent silencieusement.
exemple de maintien constantes définies:
<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 être suivie de toute autre constante définie puisqu'il est peu probable qu'elle soit établie de façon additive
j'ai seulement défini ces 2 parce que c'est surtout ce qui m'intéresse dans mon projet, ymmv.
Voir Aussi: Commune MsBuild Propriétés Du Projet
j'ai quelques suggestions pour améliorer les réponses données jusqu'à présent:
-
Utiliser La Version.CompareTo(). Les essais de l'égalité ne fonctionnera pas pour plus tard versions framework, pas encore nommé. Par exemple:
<CustomConstants Condition=" '$(TargetFrameworkVersion)' == 'v4.0' ">
ne correspond pas à v4.5 ou v4.5.1, ce que vous voulez typiquement.
-
utilisez un fichier d'importation de sorte que ces propriétés supplémentaires n'ont besoin d'être définies qu'une seule fois. Je recommande conserver le fichier des importations sous contrôle des sources, de sorte que les changements soient propagés avec les fichiers du projet, sans effort supplémentaire.
-
ajoutez l'élément import à la fin de votre fichier de projet, de sorte qu'il soit indépendant de tout groupe de propriétés spécifique à la configuration. Cela a également l'avantage d'exiger une seule ligne supplémentaire dans votre dossier de projet.
Voici le fichier d'importation (VersionSpecificSymbols.Commun.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>
Ajouter un élément D'importation au dossier de projet
référence de votre .csproj fichier en ajoutant à la fin, avant la balise.
…
<Import Project="VersionSpecificSymbols.Common.prop" />
</Project>
vous aurez besoin de corriger le chemin pour pointer vers le dossier commun/partagé où vous placez ce fichier.
Pour Utiliser Les Symboles De Temps De Compilation
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();
}
}
}
, Une Commune "La Vraie Vie" Exemple
implémenter Join (délimiteur de chaîne, IEnumerable strings) 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
Références
Évaluation De La Propriété De MSBuild
puis-je faire dépendre une directive préprocesseur de la version. NET framework?
compilation conditionnelle selon la version du cadre dans C#
je voudrais contribuer avec une réponse mise à jour qui résout certains problèmes.
si vous définissez DefineConstants à la place de CustomConstants, vous finirez, dans la ligne de commande des symboles de Compilation conditionnelle, après un changement de version du framework, avec des constantes conditionnelles dupliquées (i.e.: NETFX_451;NETFX_45;NETFX_40;NETFX_35;NETFX_30;NETFX_20;NETFX_35;NETFX_30;NETFX_20;). C'est le VersionSpecificSymbols.Commun.un accessoire qui résout tous les problèmes.
<!--
*********************************************************************
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')))) >= 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>
les symboles prédéfinis pour les cadres cibles sont maintenant intégrés dans la version de MSBuild qui est utilisée par l'outil dotnet
et à partir de VS 2017. 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
Utiliser la réflexion pour déterminer si la classe existe. Si c'est le cas, alors dynamiquement le créer et l'utiliser, sinon utiliser le .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 plus seulement:
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.