Tipos de arquivo local
Observação
Este artigo é uma especificação de recurso. A especificação serve como o documento de design para o recurso. Ele inclui mudanças de especificação propostas, juntamente com as informações necessárias durante o design e desenvolvimento do recurso. Estes artigos são publicados até que as alterações de especificações propostas sejam finalizadas e incorporadas na especificação ECMA atual.
Pode haver algumas discrepâncias entre a especificação do recurso e a implementação concluída. Essas diferenças são capturadas nas notas pertinentes da Language Design Meeting (LDM).
Você pode saber mais sobre o processo de adoção de especificações de recursos no padrão de linguagem C# no artigo sobre as especificações .
Resumo
Permita um modificador file
em declarações de tipo de nível superior. O tipo só existe no arquivo onde é declarado.
// 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.
Motivação
Nossa principal motivação é de geradores de código-fonte. Os geradores de código-fonte funcionam adicionando arquivos à compilação do usuário.
- Esses arquivos devem ser capazes de conter detalhes de implementação que estão ocultos do resto da compilação, mas são utilizáveis em todo o arquivo em que são declarados.
- Queremos reduzir a necessidade de os geradores "pesquisarem" nomes de tipos que não colidam com declarações no código do usuário ou código de outros geradores.
Projeto detalhado
- Adicionamos o modificador
file
aos seguintes conjuntos de modificadores: - O modificador
file
só pode ser usado em um tipo de nível superior.
Quando um tipo tem o modificador
Acessibilidade
O modificador file
não é classificado como um modificador de acessibilidade. Nenhum modificador de acessibilidade pode ser usado em combinação com file
em um tipo.
file
é tratado como um conceito independente da acessibilidade. Como os tipos de arquivo local não podem ser aninhados, apenas a acessibilidade padrão internal
é utilizável com tipos file
.
public file class C1 { } // error
internal file class C2 { } // error
file class C3 { } // ok
Nomeação
A implementação garante que os tipos locais de ficheiro em ficheiros diferentes com o mesmo nome serão distintos no tempo de execução. A acessibilidade do tipo e o nome nos metadados são definidos pela implementação. A intenção é permitir que o compilador adote quaisquer recursos futuros de limitação de acesso no tempo de execução que sejam adequados ao recurso. Espera-se que, na implementação inicial, uma acessibilidade internal
seja usada e um nome não pronunciável seja gerado, dependendo do ficheiro em que o tipo é declarado.
Consulta
Alteramos a secção de pesquisa de membros da seguinte forma (novo texto em negrito):
- Em seguida, se
K
for zero, todos os tipos aninhados cujas declarações incluem parâmetros de tipo serão removidos. SeK
não for zero, todos os membros com um número diferente de parâmetros de tipo serão removidos. QuandoK
é zero, os métodos com parâmetros de tipo não são removidos, uma vez que o processo de inferência de tipo (§11.6.3) pode ser capaz de inferir os argumentos de tipo.- Em seguida, deixe F ser a unidade de compilação que contém a expressão onde a pesquisa de membros está ocorrendo. Todos os membros que são tipos de arquivo local e não são declarados em F são removidos do conjunto.
- Em seguida, se o conjunto de membros acessíveis contiver tipos de arquivo local, todos os membros que não são tipos de arquivo local serão removidos do conjunto.
Comentários
Essas regras não permitem o uso de tipos locais de arquivo fora do arquivo no qual eles são declarados.
Essas regras também permitem que um tipo com escopo de ficheiro ofusque um namespace ou um tipo não com escopo de ficheiro.
// 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
}
}
Note que não atualizamos a seção dos escopos da especificação. Isto deve-se ao fato de que, como afirma a especificação:
O escopo de um nome é a região do texto do programa dentro da qual é possível referir-se à entidade declarada pelo nome sem a necessidade de qualificar o nome.
Com efeito, o âmbito afeta apenas a pesquisa de nomes não qualificados. Este não é o conceito certo para aproveitarmos, porque precisamos também impactar a pesquisa de nomes qualificados:
// 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
}
}
Portanto, não especificamos o recurso em termos de em que escopo o tipo se encontra, mas como regras adicionais de filtragem na pesquisa de membros.
Atributos
Classes locais de ficheiro podem ser tipos de atributo e podem ser usadas como atributos dentro de tipos locais de ficheiro e tipos não locais de ficheiro, como se o tipo de atributo fosse um tipo de ficheiro não local. O nome dos metadados do tipo de atributo file-local ainda passa pela mesma estratégia de geração de nome que outros tipos de arquivo local. Isso significa que detetar a presença de um tipo de arquivo local por um nome de cadeia de caracteres codificado provavelmente será impraticável, porque requer depender da estratégia de geração de nome interno do compilador, que pode mudar ao longo do tempo. No entanto, a deteção via typeof(MyFileLocalAttribute)
funciona.
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
}
}
Utilização em assinaturas
Há uma necessidade geral de impedir que tipos de arquivo local apareçam em parâmetros de membro, retornos e restrições de parâmetros de tipo em que o tipo de arquivo local pode não estar no escopo no ponto de uso do membro.
Observe que tipos não locais de arquivo têm permissão para implementar interfaces de arquivo local, semelhante a como os tipos podem implementar interfaces menos acessíveis. Dependendo dos tipos presentes nos membros da interface, isso pode resultar em uma violação das regras na seção a seguir.
Permitir o uso de assinatura apenas em membros de tipos locais de ficheiro
Talvez a maneira mais simples de garantir isso seja impor que os tipos de arquivo local só possam aparecer em assinaturas ou como tipos base de outros tipos de arquivo local:
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
}
Observe que isso restringe o uso em implementações explícitas, mesmo que tais usos sejam seguros. Fazemos isso para simplificar as regras para a iteração inicial do recurso.
file interface I
{
void M(I i);
}
class C : I
{
void I.M(I i) { } // error
}
global using static
É um erro em tempo de compilação usar um tipo local do ficheiro numa diretiva global using static
, ou seja,
global using static C; // error
file class C
{
public static void M() { }
}
Implementação/substituições
As declarações de tipo local de arquivo podem implementar interfaces, substituir métodos virtuais, etc., assim como as declarações de tipo regulares.
file struct Widget : IEquatable<Widget>
{
public bool Equals(Widget other) => true;
}
C# feature specifications