c# .net-3.5 msbuild .net-4.0

c# - Compilación condicional y objetivos del marco



.net-3.5 msbuild (6)

Hay algunos pocos lugares en los que el código de mi proyecto puede mejorar drásticamente si el marco de destino era una versión más nueva. Me gustaría poder aprovechar mejor la compilación condicional en C # para cambiarlos según sea necesario.

Algo como:

#if NET40 using FooXX = Foo40; #elif NET35 using FooXX = Foo35; #else NET20 using FooXX = Foo20; #endif

¿Alguno de estos símbolos es gratis? ¿Necesito inyectar estos símbolos como parte de la configuración del proyecto? Parece bastante fácil de hacer, ya que sabré a qué marco se dirige desde MSBuild.

/p:DefineConstants="NET40"

Actualización: Mi pregunta es ¿cómo están las personas manejando esta situación? ¿Estás creando diferentes configuraciones? ¿Estás pasando las constantes a través de la línea de comando?


@Azarien, tu respuesta se puede combinar con la de Jeremy para mantenerla en un solo lugar en lugar de Debug | Release, etc.

Para mí, la combinación de ambas variaciones funciona mejor, es decir, incluye condiciones en el código usando #if NETXX y también construyendo para diferentes versiones de framework de una vez.

Tengo estos en mi archivo .csproj:

<PropertyGroup> <DefineConstants Condition=" ''$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))'' &gt;= ''4.0'' ">NET_40_OR_GREATER</DefineConstants> </PropertyGroup> <PropertyGroup Condition=" ''$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))'' == ''3.5'' "> <DefineConstants>NET35</DefineConstants> <OutputPath>bin/$(Configuration)/$(TargetFrameworkVersion)</OutputPath> </PropertyGroup>

y en objetivos:

<Target Name="AfterBuild"> <MSBuild Condition=" ''$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))'' &gt;= ''4.0'' " Projects="$(MSBuildProjectFile)" Properties="TargetFrameworkVersion=v3.5" RunEachTargetSeparately="true" /> </Target>


Comience limpiando las constantes:

<PropertyGroup> <DefineConstants/> </PropertyGroup>

A continuación, construya su depuración, seguimiento y otras constantes como:

<PropertyGroup Condition=" ''$(Configuration)'' == ''Debug'' "> <DebugSymbols>true</DebugSymbols> <DebugType>full</DebugType> <Optimize>false</Optimize> <DefineConstants>TRACE;DEBUG;$(DefineConstants)</DefineConstants> </PropertyGroup>

Por último, crea tus constantes marco:

<PropertyGroup Condition=" ''$(TargetFrameworkVersion)'' == ''v2.0'' "> <DefineConstants>NET10;NET20;$(DefineConstants)</DefineConstants> </PropertyGroup> <PropertyGroup Condition=" ''$(TargetFrameworkVersion)'' == ''v3.0'' "> <DefineConstants>NET10;NET20;NET30;$(DefineConstants)</DefineConstants> </PropertyGroup> <PropertyGroup Condition=" ''$(TargetFrameworkVersion)'' == ''v3.5'' "> <DefineConstants>NET10;NET20;NET30;NET35;$(DefineConstants)</DefineConstants> </PropertyGroup> <PropertyGroup Condition=" ''$(TargetFrameworkVersion)'' == ''v4.0'' "> <DefineConstants>NET10;NET20;NET30;NET35;NET40;$(DefineConstants)</DefineConstants> </PropertyGroup> <PropertyGroup Condition=" ''$(TargetFrameworkVersion)'' == ''v4.5'' "> <DefineConstants>NET10;NET20;NET30;NET35;NET40;NET45;$(DefineConstants)</DefineConstants> </PropertyGroup>

Creo que este enfoque es muy legible y comprensible.


En un archivo .csproj, después de una línea existente <DefineConstants>DEBUG;TRACE</DefineConstants> , agregue esto:

<DefineConstants Condition=" ''$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))'' &gt;= ''4.0'' ">NET_40_OR_GREATER</DefineConstants> <DefineConstants Condition=" ''$(TargetFrameworkVersion.Replace(&quot;v&quot;,&quot;&quot;))'' == ''4.0'' ">NET_40_EXACTLY</DefineConstants>

Haga esto para las configuraciones de compilación Depurar y Versión. Luego usa en tu código:

#if NET_40_OR_GREATER // can use dynamic, default and named parameters #endif


Tuve problemas con estas soluciones, posiblemente porque mis constantes iniciales fueron preconstruidas por estas propiedades.

<DefineConstants /> <DefineDebug>true</DefineDebug> <DefineTrace>true</DefineTrace> <DebugSymbols>true</DebugSymbols>

Visual Studio 2010 también arrojó un error debido a los puntos y comas, alegando que son caracteres ilegales. El mensaje de error me dio una pista ya que podía ver las constantes preconstruidas separadas por comas, seguidas eventualmente por mi punto "ilegal". Después de reformatear y masajear, pude encontrar una solución que funcionaba para mí.

<PropertyGroup> <!-- Adding a custom constant will auto-magically append a comma and space to the pre-built constants. --> <!-- Move the comma delimiter to the end of each constant and remove the trailing comma when we''re done. --> <DefineConstants Condition=" !$(DefineConstants.Contains('', NET'')) ">$(DefineConstants)$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", "")), </DefineConstants> <DefineConstants Condition=" $(DefineConstants.Contains('', NET'')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(", NET"))))$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", "")), </DefineConstants> <DefineConstants Condition=" $(TargetFrameworkVersion.Replace(''v'', '''')) >= 2.0 ">$(DefineConstants)NET_20_OR_GREATER, </DefineConstants> <DefineConstants Condition=" $(TargetFrameworkVersion.Replace(''v'', '''')) >= 3.5 ">$(DefineConstants)NET_35_OR_GREATER</DefineConstants> <DefineConstants Condition=" $(DefineConstants.EndsWith('', '')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(", "))))</DefineConstants> </PropertyGroup>

Publicaba una captura de pantalla del cuadro de diálogo Configuración avanzada del compilador (que se abre haciendo clic en el botón "Opciones de compilación avanzadas ..." en la pestaña Compilar de su proyecto). Pero como nuevo usuario, me falta el representante para hacerlo. Si pudieras ver la captura de pantalla, verías las constantes personalizadas rellenas automáticamente por el grupo de propiedades y luego estarías diciendo: "Tengo que conseguirme algo de eso".


EDITAR: Obtuve ese representante sorprendentemente rápido .. ¡Gracias chicos! Aquí está esa captura de pantalla:


Una alternativa que me funciona hasta ahora es agregar lo siguiente al archivo del proyecto:

<PropertyGroup> <DefineConstants Condition=" !$(DefineConstants.Contains('';NET'')) ">$(DefineConstants);$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants> <DefineConstants Condition=" $(DefineConstants.Contains('';NET'')) ">$(DefineConstants.Remove($(DefineConstants.LastIndexOf(";NET"))));$(TargetFrameworkVersion.Replace("v", "NET").Replace(".", ""))</DefineConstants> </PropertyGroup>

Esto toma el valor de la propiedad TargetFrameworkVersion, que es como "v3.5", reemplaza a "v" y "." para obtener "NET35" (usando la nueva característica Funciones de propiedad ). A continuación, elimina cualquier valor de "NETxx" existente y lo agrega al final de DefininedConstants. Puede ser posible simplificar esto, pero no tengo tiempo para jugar.

Si observa la pestaña Generar de las propiedades del proyecto en VS, verá el valor resultante en la sección de símbolos de compilación condicional. Al cambiar la versión del marco de destino en la pestaña Aplicación, el símbolo cambia automáticamente. A continuación, puede utilizar las directivas del preprocesador #if NETxx de la forma habitual. Cambiar el proyecto en VS no parece perder el CustomGroup personalizado.

Tenga en cuenta que esto no parece ofrecerle algo diferente para las opciones de destino del perfil de cliente, pero eso no es un problema para mí.


Una de las mejores formas de lograr esto es crear diferentes configuraciones de compilación en su proyecto:

<PropertyGroup Condition=" ''$(Framework)'' == ''NET20'' "> <DefineConstants>NET20</DefineConstants> <OutputPath>bin/$(Configuration)/$(Framework)</OutputPath> </PropertyGroup> <PropertyGroup Condition=" ''$(Framework)'' == ''NET35'' "> <DefineConstants>NET35</DefineConstants> <OutputPath>bin/$(Configuration)/$(Framework)</OutputPath> </PropertyGroup>

Y en una de tus configuraciones predeterminadas:

<Framework Condition=" ''$(Framework)'' == '''' ">NET35</Framework>

Que establecería el valor predeterminado si no estuviera definido en otro lugar. En el caso anterior, OutputPath le proporcionará un ensamblaje por separado cada vez que construya cada versión.

Luego crea un objetivo AfterBuild para compilar tus diferentes versiones:

<Target Name="AfterBuild"> <MSBuild Condition=" ''$(Framework)'' != ''NET20''" Projects="$(MSBuildProjectFile)" Properties="Framework=NET20" RunEachTargetSeparately="true" /> </Target>

Este ejemplo recompilará todo el proyecto con la variable de Framework establecida en NET20 después de la primera compilación (compilando ambos y asumiendo que la primera compilación fue la NET35 predeterminada desde arriba). Cada compilación tendrá los valores de definición condicional establecidos correctamente.

De esta manera, incluso puede excluir ciertos archivos en el archivo de proyecto si quiere sin tener que #ifdef los archivos:

<Compile Include="SomeNet20SpecificClass.cs" Condition=" ''$(Framework)'' == ''NET20'' " />

o incluso referencias

<Reference Include="Some.Assembly" Condition="" ''$(Framework)'' == ''NET20'' " > <HintPath>../Lib/$(Framework)/Some.Assembly.dll</HintPath> </Reference>