Detectar la versión del marco de destino en tiempo de comstackción

Tengo un código que hace uso de los Métodos de extensión, pero comstack bajo .NET 2.0 usando el comstackdor en VS2008. Para facilitar esto, tuve que declarar ExtensionAttribute:

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

Sin embargo, ahora me gustaría que la biblioteca en la que está contenida esa clase también sea comstackble bajo .NET 3.0, 3.5 y 4.0, sin la advertencia ‘ExtensionAttribute se define en varios lugares’.

¿Hay alguna directiva de tiempo de comstackción que pueda usar para incluir solo ExtensionAttribute cuando la versión de framework que se está orientando es .NET 2?

La pregunta SO vinculada con ‘crear N configuraciones diferentes’ es ciertamente una opción, pero cuando tuve una necesidad de esto, simplemente agregué elementos condicionales DefinineConstants, así que en mi Debug | x86 (por ejemplo) después de DefineConstants para DEBUG; TRACE, Agregué estos 2, comprobando el valor en TFV que se estableció en el primer PropertyGroup del archivo csproj.

 RUNNING_ON_4 NOT_RUNNING_ON_4 

No necesitas ambas cosas, obviamente, pero está ahí para dar ejemplos de comportamientos eq y ne – #else y #elif también funcionan bien 🙂

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

Podría cambiar entre la orientación 3.5 y 4.0 y haría lo correcto.

Los grupos de propiedades solo sobrescriben, por lo que esto anularía la configuración de DEBUG , TRACE u otros. – Ver la evaluación de propiedad de MSBuild

Además, si la propiedad DefineConstants se establece desde la línea de comandos, cualquier cosa que se haga dentro del archivo del proyecto es irrelevante ya que esa configuración se convierte en global de solo lectura. Esto significa que sus cambios a ese valor fallan silenciosamente.

Ejemplo de mantenimiento de constantes definidas existentes:

  V2 V4 $(DefineConstants); $(DefineConstants)$(CustomConstants) 

Esta sección DEBE venir después de cualquier otra constante definida, ya que es poco probable que se configuren de manera aditiva

Solo definí esos 2 porque eso es principalmente lo que me interesa de mi proyecto, mmm.

Ver también: Propiedades comunes del proyecto MsBuild

Tengo algunas sugerencias para mejorar las respuestas dadas hasta ahora:

  1. Use Version.CompareTo (). Las pruebas de igualdad no funcionarán para versiones posteriores del framework, que aún no se han nombrado. P.ej

      

    no coincidirá con v4.5 o v4.5.1, lo que normalmente desea.

  2. Use un archivo de importación para que estas propiedades adicionales solo tengan que definirse una vez. Recomiendo mantener el archivo de importación bajo control de fuente, para que los cambios se propaguen junto con los archivos del proyecto, sin esfuerzo adicional.

  3. Agregue el elemento de importación al final de su archivo de proyecto, para que sea independiente de cualquier grupo de propiedades específico de la configuración. Esto también tiene el beneficio de requerir una sola línea adicional en su archivo de proyecto.

Aquí está el archivo de importación (VersionSpecificSymbols.Common.prop)

    $(DefineConstants);NETFX_451 $(DefineConstants);NETFX_45 $(DefineConstants);NETFX_40 $(DefineConstants);NETFX_35 $(DefineConstants);NETFX_30   

Agregar elemento de importación al archivo de proyecto

Refiéralo desde su archivo .csproj agregando al final, antes de la etiqueta.

   

Tendrá que arreglar la ruta para que apunte a la carpeta común / compartida donde puso este archivo.

Para usar los símbolos de tiempo de comstackción

 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(); } } } 

Un ejemplo común de “vida real”

Implementando Join (string delimiter, IEnumerable strings) Antes de .NET 4.0

 // string Join(this IEnumerable 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 strings) { return string.Join(delimiter, strings.ToArray()); } #endif 

Referencias

Funciones de propiedad

Evaluación de propiedad de MSBuild

¿Puedo hacer que una directiva de preprocesador dependa de la versión de .NET Framework?

Comstackción condicional según la versión del marco en C #

Me gustaría contribuir con una respuesta actualizada que resuelva algunos problemas.

Si configura DefineConstants en lugar de CustomConstants terminará, en la línea de comandos Debug de Conditional Comstacktion, después de algún cambio de versión de framework, con constantes condicionales duplicadas (es decir: NETFX_451; NETFX_45; NETFX_40; NETFX_35; NETFX_30; NETFX_20; NETFX_35; NETFX_30 ; NETFX_20;). Este es el VersionSpecificSymbols.Common.prop que resuelve cualquier problema.

      $(CustomConstants);NETFX_20     $(CustomConstants);NETFX_30 $(CustomConstants);NETFX_20     $(CustomConstants);NETFX_35 $(CustomConstants);NETFX_30 $(CustomConstants);NETFX_20     $(CustomConstants);NETFX_451 $(CustomConstants);NETFX_45 $(CustomConstants);NETFX_40 $(CustomConstants);NETFX_35 $(CustomConstants);NETFX_30 $(CustomConstants);NETFX_20     $(DefineConstants);$(CustomConstants)   

Los símbolos predefinidos para los marcos de destino ahora están integrados en la versión de MSBuild que utilizan la herramienta de dotnet y VS 2017 en adelante. Consulte https://docs.microsoft.com/en-us/dotnet/standard/frameworks#how-to-specify-target-frameworks para obtener la lista completa.

 #if NET47 Console.WriteLine("Running on .Net 4.7"); #elif NETCOREAPP2_0 Console.WriteLine("Running on .Net Core 2.0"); #endif 

Use la reflexión para determinar si la clase existe. Si lo hace, entonces cree y use dinámicamente , de lo contrario use la clase de solución .Net2 que se puede definir, pero que no se usa para todas las otras versiones .net.

Aquí está el código que utilicé para una AggregateException que es .Net 4 y superior solamente:

 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.