Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Nota:
Este artículo es una especificación de características. La especificación actúa como documento de diseño de la característica. Incluye cambios de especificación propuestos, junto con la información necesaria durante el diseño y el desarrollo de la característica. Estos artículos se publican hasta que se finalizan los cambios de especificación propuestos y se incorporan en la especificación ECMA actual.
Puede haber algunas discrepancias entre la especificación de características y la implementación completada. Esas diferencias se recogen en las notas de la reunión de diseño de lenguaje (LDM) correspondientes.
Puede obtener más información sobre el proceso de adopción de especificaciones de características en el estándar del lenguaje C#, en el artículo sobre especificaciones.
Problema planteado por experto: https://github.com/dotnet/csharplang/issues/5529
Resumen
Permita un modificador file en declaraciones de tipo de nivel superior. El tipo solo existe en el archivo donde se declara.
// File1.cs
namespace NS;
file class Widget
{
}
// File2.cs
namespace NS;
file class Widget // different symbol than the Widget in File1
{
}
// File3.cs
using NS;
var widget = new Widget(); // error: The type or namespace name 'Widget' could not be found.
Motivación
Nuestra motivación principal proviene de los generadores de código fuente. Los generadores de código fuente funcionan agregando archivos a la compilación del usuario.
- Esos archivos deben ser capaces de incluir los detalles de la implementación que están ocultos del resto de la compilación, pero se pueden usar en todo el archivo en el que se declaran.
- Queremos que los generadores no tengan que "buscar" siempre los nombres de los tipos que no entren en conflicto con declaraciones en el código de usuario o el código de otros generadores.
Diseño detallado
- Hemos añadido el modificador
filea los siguientes grupos de modificadores: - El modificador
filesolo se puede usar en un tipo de nivel superior.
Cuando un tipo tiene el modificador file, se dice que es un tipo local de archivo.
Accesibilidad
El modificador file no se clasifica como un modificador de accesibilidad. No se pueden usar modificadores de accesibilidad en combinación con file en un tipo.
file se considera un concepto independiente de accesibilidad. Dado que los tipos locales de archivo no se pueden anidar, solo el internal de accesibilidad predeterminada se puede usar con tipos file.
public file class C1 { } // error
internal file class C2 { } // error
file class C3 { } // ok
Nomenclatura
La implementación garantiza que, para el tiempo de ejecución, los tipos locales de archivo en diferentes archivos con el mismo nombre serán distintos. La accesibilidad y el nombre del tipo en los metadatos se definen en la implementación. Lo que se pretende es permitir que el compilador adopte las funcionalidades futuras de limitación de acceso en el tiempo de ejecución que sean adecuadas para esta función. Se espera que, en la implementación inicial, se use una accesibilidad internal y se emplee un nombre generado que no se puede describir, lo cual depende del archivo en el que se declare el tipo.
Búsqueda
Modificamos la sección de búsqueda de miembros de la siguiente manera (nuevo texto en negrita):
- A continuación, si
Kes cero, se eliminan todos los tipos anidados cuyas declaraciones incluyan parámetros de tipo. SiKno es cero, se eliminan todos los miembros con un número diferente de parámetros de tipo. CuandoKes cero, los métodos que tienen parámetros de tipo no se eliminan, ya que el proceso de inferencia de tipos (sección 11.6.3) podría ser capaz de inferir los argumentos de tipo.- A continuación, sea F la unidad de compilación que contiene la expresión en la que se realiza la búsqueda de miembros. Todos los miembros que sean tipos locales del archivo y no se declaren en F se quitan del grupo.
- Después, si el grupo de miembros accesibles incluye tipos locales de archivo, todos los miembros que no sean tipos locales de archivo se quitarán del grupo.
Observaciones
Estas reglas no permiten usar tipos locales de archivo fuera del archivo en el que se declaran.
Estas reglas también permiten que un tipo de archivo local sobrescriba un espacio de nombres o un tipo que no sea de archivo:
// File1.cs
class C
{
public static void M() { }
}
// File2.cs
file class C
{
public static void M() { }
}
class Program
{
static void Main()
{
C.M(); // refers to the 'C' in File2.cs
}
}
Tenga en cuenta que no actualizamos la sección de los ámbitos de la especificación. Esto se debe a que, como indica la especificación:
El ámbito de un nombre es la región del texto del programa en el que es posible hacer referencia a la entidad declarada a través del nombre sin cualificación del nombre.
En efecto, el ámbito solo afecta a la búsqueda de nombres no calificados. Este concepto no es del todo el que debemos hacer uso, ya que también hay que hacer que se aplique en la búsqueda de nombres calificados.
// File1.cs
namespace NS1
{
file class C
{
public static void M() { }
}
}
namespace NS2
{
class Program
{
public static void M()
{
C.M(); // error: C is not in scope
NS1.C.M(); // ok: C can be accessed through NS1.
}
}
}
// File2.cs
namespace NS1
{
class Program
{
C.M(); // error
NS1.C.M(); // error
}
}
Por ello, no especificamos la funcionalidad en relación con el ámbito en el que se encuentre el tipo, sino como reglas de filtrado adicionales en la búsqueda de miembros.
Atributos
Las clases locales de archivo pueden ser tipos de atributo y se pueden usar como atributos dentro de tipos locales de archivo y tipos no locales de archivo, como si el tipo de atributo fuera de un tipo local de archivo. El nombre de los metadatos del tipo de atributo local de archivo sigue pasando por el mismo proceso de generación de nombres que otros tipos locales de archivo. Esto significa que es probable que la detección de la presencia de un tipo local de archivo mediante un nombre de cadena codificado de forma rígida sea poco práctico, ya que se necesita que dependa del proceso de generación interna de nombres del compilador, que podría cambiar con el tiempo. Sin embargo, sí funciona la detección a través de typeof(MyFileLocalAttribute).
using System;
using System.Linq;
file class MyFileLocalAttribute : Attribute { }
[MyFileLocalAttribute]
public class C
{
public static void Main()
{
var attribute = typeof(C).CustomAttributes.Where(attr => attr.AttributeType == typeof(MyFileLocalAttribute)).First();
Console.Write(attribute); // outputs the generated name of the file-local attribute type
}
}
Uso en firmas
Lo más normal es que se necesite evitar que los tipos locales de archivo aparezcan en parámetros de miembro, devoluciones y restricciones de parámetros de tipo donde el tipo local de archivo podría no estar en el ámbito en el momento de uso del miembro.
Tenga en cuenta que los tipos no locales de archivo pueden implementar interfaces locales de archivo, de forma similar a la manera en que los tipos pueden implementar interfaces menos accesibles. Según los tipos presentes en los miembros de la interfaz, esto podría hacer que se vulneren las reglas de la sección siguiente.
Permitir solo el uso de firmas en miembros de tipos locales de archivo
Quizás la manera más sencilla de garantizar esto es exigir que los tipos locales de archivo solo puedan aparecer en firmas o como tipos base de otros tipos locales de archivo:
file class FileBase
{
}
public class Derived : FileBase // error
{
private FileBase M2() => new FileBase() // error
}
file class FileDerived : FileBase // ok
{
private FileBase M2() => new FileBase(); // ok
}
Tenga en cuenta que esto restringe el uso en implementaciones explícitas, aunque dichos usos sean seguros. Lo hacemos para simplificar las reglas de la iteración inicial de la funcionalidad.
file interface I
{
void M(I i);
}
class C : I
{
void I.M(I i) { } // error
}
global using static
Se trata de un error en tiempo de compilación para usar un tipo local de archivo en una directiva global using static, por ejemplo.
global using static C; // error
file class C
{
public static void M() { }
}
Implementación/anulaciones
Las declaraciones de tipos locales de archivo pueden implementar interfaces, invalidar métodos virtuales, etc. al igual que las declaraciones de tipos normales.
file struct Widget : IEquatable<Widget>
{
public bool Equals(Widget other) => true;
}
C# feature specifications