¿C # 8 es compatible con .NET Framework?

131

En la configuración de compilación avanzada de Visual Studio 2019, C # 8 no parece estar disponible para un proyecto de .NET Framework, solo (como en la imagen a continuación) para un proyecto de .NET Core 3.0:

ingrese la descripción de la imagen aquí

¿C # 8 es compatible con .NET Framework?

James Harcourt
fuente

Respuestas:

238

Sí, C # 8 se puede usar con .NET Framework y otros destinos anteriores a .NET Core 3.0 / .NET Standard 2.1 en Visual Studio 2019 (o versiones anteriores de Visual Studio si instala un paquete Nuget ).

La versión de idioma debe establecerse en 8.0en el archivo csproj.

La mayoría de las funciones, pero no todas, están disponibles en cualquier marco de referencia.


Características que funcionan

Las siguientes funciones son solo cambios de sintaxis; funcionan independientemente del marco:

Funciones que se pueden hacer funcionar

Estos requieren nuevos tipos que no están en .NET Framework. Solo se pueden usar junto con paquetes o archivos de código "polyfill" Nuget:

Miembros de interfaz predeterminados: no funcionan

Los miembros de la interfaz predeterminada no se compilarán en .NET Framework y nunca funcionarán porque requieren cambios de tiempo de ejecución en CLR. El .NET CLR ahora está congelado, ya que .NET Core es ahora el camino a seguir.

Para obtener más información sobre lo que funciona y lo que no, y sobre posibles polyfills, consulte el artículo de Stuart Lang, C # 8.0 y .NET Standard 2.0 - Doing Unsupported Things .


Código

El siguiente proyecto de C # que tiene como destino .NET Framework 4.8 y utiliza tipos de referencia que aceptan valores NULL C # 8 se compila en Visual Studio 16.2.0. Lo creé eligiendo la plantilla Biblioteca de clases estándar de .NET y luego editándola para apuntar a .NET Framework en su lugar:

.csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>net48</TargetFrameworks>
    <LangVersion>8.0</LangVersion>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>

.cs:

namespace ClassLibrary1
{
    public class Class1
    {
        public string? NullableString { get; set; }
    }
}

Luego probé un proyecto de .NET Framework 4.5.2 WinForms, usando un .csprojformato heredado , y agregué la misma propiedad de tipo de referencia anulable. Cambié el tipo de idioma en el cuadro de diálogo de configuración de Visual Studio Advanced Build (deshabilitado en 16.3) latesty guardé el proyecto. Por supuesto que este punto no se construye. Abrí el archivo del proyecto en un editor de texto y cambié latesta previewen la configuración de compilación PropertyGroup:

<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
   <LangVersion>preview</LangVersion>

Luego habilité la compatibilidad con tipos de referencia que aceptan valores NULL agregando <Nullable>enable</Nullable>a la principal PropertyGroup:

<PropertyGroup>
   <Nullable>enable</Nullable>

Recargué el proyecto y se compila.


Los detalles sangrientos

Cuando se escribió esta respuesta por primera vez, C # 8 estaba en vista previa y se involucró mucho trabajo de detective. Aquí dejo esa información para la posteridad. Siéntase libre de omitirlo si no necesita conocer todos los detalles sangrientos.

Históricamente, el lenguaje C # ha sido mayoritariamente neutral en cuanto al marco , es decir, capaz de compilar versiones anteriores del marco, aunque algunas características han requerido nuevos tipos o compatibilidad con CLR.

La mayoría de los entusiastas de C # habrán leído la entrada del blog Building C # 8.0 de Mads Torgersen, que explica que ciertas características de C # 8 tienen dependencias de plataforma:

Los flujos, indexadores y rangos asíncronos se basan en nuevos tipos de marcos que serán parte de .NET Standard 2.1 ... .NET Core 3.0, así como Xamarin, Unity y Mono implementarán .NET Standard 2.1, pero .NET Framework 4.8 lo hará no. Esto significa que los tipos necesarios para utilizar estas funciones no estarán disponibles en .NET Framework 4.8.

Esto se parece un poco a las tuplas de valor que se introdujeron en C # 7. Esa característica requería nuevos tipos, las ValueTupleestructuras, que no estaban disponibles en las versiones de NET Framework por debajo de 4.7 o .NET Standard anteriores a 2.0. Sin embargo , C # 7 aún podría usarse en versiones anteriores de .NET, ya sea sin tuplas de valor o con ellas instalando el paquete System.ValueTuple Nuget . Visual Studio entendió esto y todo estaba bien con el mundo.

Sin embargo, Mads también escribió:

Por esta razón, el uso de C # 8.0 solo se admite en plataformas que implementan .NET Standard 2.1.

... que, de ser cierto, habría descartado el uso de C # 8 con cualquier versión de .NET Framework, y de hecho incluso en las bibliotecas .NET Standard 2.0 que solo recientemente se nos animó a usar como destino de referencia para el código de la biblioteca. Ni siquiera podría usarlo con versiones de .NET Core anteriores a 3.0, ya que también son compatibles con .NET Standard 2.0.

¡La investigación estaba en marcha! -

  • Jon Skeet tiene una versión alfa de Noda-Time que usa C # 8 lista para usar y que se dirige solo a .NET Standard 2.0. Claramente espera que C # 8 / .NET Standard 2.0 sea compatible con todos los marcos de la familia .NET. (Consulte también la publicación del blog de Jon "Primeros pasos con tipos de referencia que aceptan valores NULL" ).

  • Los empleados de Microsoft han estado discutiendo la interfaz de usuario de Visual Studio para los tipos de referencia que aceptan valores NULL de C # 8 en GitHub , y se afirma que tienen la intención de admitir el legado csproj(formato del SDK anterior a .NET Core csproj). Esta es una indicación muy clara de que C # 8 se podrá utilizar con .NET Framework. [Sospecho que retrocederán en esto ahora que el menú desplegable de la versión de idioma de Visual Studio 2019 se ha deshabilitado y .NET se ha vinculado a C # 7.3]

  • Poco después de la famosa publicación del blog, un hilo de GitHub discutió el soporte multiplataforma. Un punto importante que surgió fue que .NET Standard 2.1 incluirá un marcador que indica que se admiten implementaciones predeterminadas de interfaces ; la función requiere un cambio de CLR que nunca estará disponible para .NET Framework. Aquí está lo importante, de Immo Landwerth, administrador de programas en el equipo .NET de Microsoft:

    Se espera que los compiladores (como C #) utilicen la presencia de este campo para decidir si permitir o no implementaciones de interfaz predeterminadas. Si el campo está presente, se espera que el tiempo de ejecución pueda cargar y ejecutar el código resultante.

  • Todo esto apuntaba a que "C # 8.0 solo es compatible con plataformas que implementan .NET Standard 2.1" siendo una simplificación excesiva, y que C # 8 admitirá .NET Framework pero, como hay tanta incertidumbre, pregunté en GitHub y HaloFour respondió:

    IIRC, la única característica que definitivamente no aparecerá en .NET Framework es DIM (métodos de interfaz predeterminados) ya que requiere cambios en el tiempo de ejecución. Las otras características están impulsadas por la forma de las clases que es posible que nunca se agreguen a .NET Framework, pero que se pueden rellenar mediante su propio código o NuGet (rangos, índices, iteradores asíncronos, eliminación asíncrona).

  • Victor Derks comentó que "Los nuevos atributos que aceptan valores NULL necesarios para diseñar los casos de uso que aceptan valores NULL más complejos solo están disponibles en System.Runtime.dll que se envía con .NET Core 3.0 y .NET Standard 2.1 ... [y] incompatible con .NET Framework 4.8 "

  • Sin embargo, Immo Landwerth comentó que "La gran mayoría de nuestras API no necesitaban atributos personalizados, ya que los tipos son completamente genéricos o no nulos" en el artículo Pruebe los tipos de referencia que aceptan valores NULL.

  • Ben Hall planteó el problema de Disponibilidad de atributos que aceptan valores NULL fuera de Core 3.0 en GitHub, destacando los siguientes comentarios de los empleados de Microsoft:

C # 8 será totalmente compatible con .net core 3.0 y .net estándar 2.1 únicamente. Si edita manualmente el archivo de proyecto para usar C # 8 con .net core 2.1, se encuentra en territorio no admitido. Algunas características de C # 8 funcionarán bien, algunas características de C # 8 no funcionarán demasiado bien (por ejemplo, un rendimiento deficiente), algunas características de C # 8 funcionarán con hacks adicionales y algunas características de C # 8 no funcionarán en absoluto. Muy complejo de explicar. No lo bloqueamos activamente para que los usuarios expertos que puedan navegar por él puedan hacerlo. No recomendaría este mix & match no admitido para un uso amplio.

(Jan Kotas)

Las personas como usted que están dispuestas a entender y trabajar con ellas son libres de usar C # 8. El punto es que no todas las características del lenguaje funcionarán en objetivos de nivel inferior.

(Immo Landwerth)


Visual Studio 2019

Ha habido un cambio importante en la versión RTM de Visual Studio 2019 versión 16.3: la versión de lanzamiento para C # 8.0: el menú desplegable de selección de idioma se ha deshabilitado:

ingrese la descripción de la imagen aquí

El fundamento de Microsoft para esto es:

En el futuro, ... cada versión de cada marco tendrá una única versión compatible y predeterminada, y no admitiremos versiones arbitrarias. Para reflejar este cambio en el soporte, esta confirmación deshabilita permanentemente el cuadro combinado de versión de idioma y agrega un enlace a un documento que explica el cambio.

El documento que se abre es el control de versiones en lenguaje C # . Esto enumera C # 8.0 como el idioma predeterminado para .NET Core 3.x SOLAMENTE. También confirma que , en el futuro, cada versión de cada marco tendrá una única versión compatible y predeterminada y que ya no se puede confiar en el agnosticismo del marco del lenguaje.

La versión de idioma aún se puede forzar a 8 para proyectos de .NET Framework editando el archivo .csproj.


Advertencia emptor

Microsoft no admite oficialmente la combinación C # 8 / .NET Framework. Es, dicen, solo para expertos.

Stephen Kennedy
fuente
3
Esto debería aclarar cualquier confusión derivada del hecho de que podemos, si lo intentamos, usar algunas características de C # 8 fuera del Estándar 2.1 - github.com/dotnet/corefx/issues/40039
Ben Hall
2
Los nuevos atributos que aceptan valores NULL ( docs.microsoft.com/en-us/dotnet/csharp/nullable-attributes ) necesarios para diseñar los casos de uso que aceptan valores NULL más complejos solo están disponibles en System.Runtime.dll que se envía con .NET Core 3.0 y. NET estándar 2.1. Esto hace que \ C # 8.0 anulable sea incompatible con, NET Framework 4.8
Victor Derks
3
@BenHall He añadido algunas conclusiones de su problema. Muchas gracias por plantear el problema y publicarlo aquí. Siéntase libre de editar la respuesta si es incorrecta.
Stephen Kennedy
3
Visual Studio 2019 IntelliSense no admite tipos de referencia que aceptan valores NULL cuando se especifica a través <Nullable>enable</Nullable>de csproj. Parece funcionar cuando se usan #nullable enabledirectivas. Ver también: github.com/dotnet/project-system/issues/5551
Bouke
3
@odalet No tendría ningún reparo en apuntar a C # 8 y usar las características básicas que no requieren polyfills (ya lo hago), y posiblemente con los polyfills también (no los he necesitado). Sin embargo, el mejor consejo que puedo darte es: si tienes dudas, no lo hagas, al menos no si tu trabajo depende de ello.
Stephen Kennedy
34

Según esta entrada de blog, el lenguaje está realmente vinculado al marco:

Esto significa que los tipos necesarios para utilizar estas funciones no estarán disponibles en .NET Framework 4.8. Del mismo modo, las implementaciones de miembros de la interfaz predeterminada se basan en nuevas mejoras en tiempo de ejecución, y tampoco las haremos en .NET Runtime 4.8.

Por esta razón, el uso de C # 8.0 solo se admite en plataformas que implementan .NET Standard 2.1. La necesidad de mantener estable el tiempo de ejecución nos ha impedido implementar nuevas funciones de lenguaje durante más de una década. Con la naturaleza de lado a lado y de código abierto de los tiempos de ejecución modernos, creemos que podemos hacerlos evolucionar de manera responsable nuevamente, y diseñar el lenguaje con eso en mente. Scott explicó en su Actualización sobre .NET Core 3.0 y .NET Framework 4.8 que .NET Framework verá menos innovación en el futuro, en lugar de centrarse en la estabilidad y la confiabilidad. Teniendo en cuenta eso, creemos que es mejor que se pierda algunas funciones del idioma que que nadie las obtenga.

user1781290
fuente
3
Muchos más detalles en la otra respuesta de Stephen Kennedy. De hecho, es bastante fácil hacer que un subconjunto sustancial de C # 8.0 funcione cuando se apunta a .NET Framework. Pero algunas partes de C # 8.0 requieren cambios en el tiempo de ejecución que Microsoft no va a realizar para el "antiguo" .NET Framework. Y parecen estar vinculando la versión de idioma y la versión de .NET más estrechamente.
Jeppe Stig Nielsen
1

C # 8.0 (y versiones posteriores) solo se admite en .NET Core 3.xy versiones más recientes. Muchas de las características más nuevas requieren características de biblioteca y tiempo de ejecución introducidas en .NET Core 3.x: control de versiones del lenguaje C #

Zdravko Zdravkov
fuente
2
¿Ha visto la respuesta marcada como correcta de @stephen kennedy arriba?
James Harcourt