Tipos locales de archivo
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 e 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 las especificaciones de .
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 origen funcionan agregando archivos a la compilación del usuario.
- Esos archivos deben ser capaces de contener detalles de 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 reducir la necesidad de que los generadores "busquen" nombres de tipo que no entren en conflicto con declaraciones en el código de usuario o el código de otros generadores.
Diseño detallado
- Agregamos el modificador
file
a los siguientes conjuntos de modificadores: - El modificador
file
solo 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 trata como 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
Nombramiento
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. La intención es permitir que el compilador adopte las características futuras de limitación de acceso en el tiempo de ejecución que sean adecuadas para la característica. 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
K
es cero, se quitan todos los tipos anidados cuyas declaraciones incluyen parámetros de tipo. SiK
no es cero, se quitan todos los miembros con un número diferente de parámetros de tipo. CuandoK
es cero, los métodos que tienen parámetros de tipo no se quitan, ya que el proceso de inferencia de tipos (§11.6.3) podría 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 son tipos locales de archivo y no se declaran en F se quitan del conjunto.
- Siguiente, si el conjunto de miembros accesibles contiene tipos locales de archivo, todos los miembros que no son tipos locales de archivo se quitan del conjunto.
Observaciones
Estas reglas no permiten el uso de 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 por el 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 metadatos del tipo de atributo local de archivo sigue pasando por la misma estrategia 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 por un nombre de cadena codificado de forma rígida sea poco práctico, ya que requiere en función de la estrategia de generación de nombres interna del compilador, que puede 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. Dependiendo de los tipos presentes en los miembros de la interfaz, podría provocar una infracción de 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 asegurarse de que esto es aplicar que los tipos locales de archivo solo pueden 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 estos usos sean seguros. Lo hacemos para simplificar las reglas de la iteración inicial de la característica.
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 tipo local 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