C# 11 -  O modificador file


  Hoje veremos o novo recurso do C# 11 :  o modificador file.

Uma das novidades do C#11  é a inclusão do novo recurso de tipos com escopo de arquivo através do modificador file.

Assim foi introduzido o novo modificador file que pode ser aplicado a qualquer definição de tipo para restringir o seu uso ao arquivo atual. Desta forma podemos ter várias classes com o mesmo nome em um único projeto.

Para ilustrar o uso deste recurso vamos criar um projeto Console chamado AppConsole1 usando o Visual Studio 2022 Preview com o .NET 7 RC 2 instalado.

Neste projeto vamos criar a mesma classe Resposta em dois arquivos distintos : Arquivo1.cs e Arquivo2.cs

1- Arquivo1.cs

namespace AppConsole1;

 file static class Resposta
 {
  
internal static string GetFileScopeSecreto()
        =>
"Resposta do Arquivo1.cs"
;
 }

 static class ClasseInternaArquivo1
 {
   
internal static string
GetString()
       => Resposta.GetFileScopeSecreto();
 }
 

Aqui usamos o modificador file na classe Resposta em Arquivo1.

2- Arquivo2.cs

namespace AppConsole1;

 file static class Resposta
 {
  
internal static string GetFileScopeSecreto()
        =>
"Resposta do Arquivo2.cs"
;
 }

 static class ClasseInternaArquivo2
 {
   
internal static string
GetString()
       => Resposta.GetFileScopeSecreto();
 }
 

Aqui usamos o modificador file em uma classe com o nome Resposta(o mesmo nome usado em Arquivo1) em Arquivo2.

A seguir na classe Program vamos incluir o código abaixo:

using AppConsole1;
using
static
System.Console;

WriteLine(ClasseInternaArquivo1.GetString());

WriteLine(ClasseInternaArquivo2.GetString());

ReadKey();
 

Executando o projeto teremos o resultado a seguir:

Desta forma vemos que os tipos com o modificador file podem ser acessados indiretamente fora de seu arquivo de origem não havendo conflitos de nomes.

No código acima,  contamos com as classes internas ClasseInternaArquivo1 e ClasseInternaArquivo2 para executar ambas as classes Resposta na classe Program.

A classe Resposta onde usamos o modificador file também pode ser usada indiretamente fora de seu arquivo de origem por meio de uma interface da seguinte forma.

Aqui estamos substituindo o código de Arquivo1.cs pelo código abaixo:

namespace AppConsole1;

 file class Resposta : IResposta
 {
  
public string GetFileScopeSecreto() => "Resposta de Arquivo1.cs";
 }


 internal
interface IResposta
 {
  
string GetFileScopeSecreto();
 }


 static
class ClasseInternaArquivo1
 {
 
internal static IResposta GetString() => new
Resposta();
 }
 

A classe Program vai continuar funcionando da mesma forma.

Agora, se tentarmos acessar diretamente a classe Resposta fora do arquivo onde foi declarada não teremos acesso devido à restrição do escopo de visibilidade estar restrito somente ao arquivo.

Acima ao tentar acessar a classe Resposta no arquivo Program, mesmo definindo o namespace, não teremos acesso.

Alguns fatos sobre o uso do modificador file :

Quais as motivações da implementação deste recurso ?

O uso dos namespaces continua sendo a maneira preferida de evitar a colisão de nomes de tipos. No entanto, podemos imaginar vários casos de uso para esse recurso nos seguintes cenários :

Código gerado:  Os templates gerados normalmente usam o mesmo nome de classe repetidamente, como Item , Info ou DataSet. Para evitar colisões, temos que aninhar uma classe Item em uma classe pai ou manter um índice no nome para cada versão gerada, como Item1, Item2... Assim, o modificador file pode facilitar significativamente a geração de código.

Métodos de extensão: O uso de métodos de extensão é hoje em dia uma construção de linguagem popular para desenvolvedores C#. No entanto, pode haver colisão de nomenclatura do método de extensão. Com essa restrição de visibilidade de arquivo, agora é fácil ter métodos de extensão restritos a um único arquivo. Outro problema comum com os métodos de extensão - resolvido por essa restrição de visibilidade de arquivo - é que eles poluem as listas suspensas do intellisense.

Classes aninhadas : Para resolver a colisão de nomes dentro de um projeto, uma solução comum é declarar classes aninhadas privadas. No entanto, este não é um código limpo porque um nível de recuo extra é adicionado, e, muito recuo atrapalha o código. Assim, a palavra-chave file pode ajudar a contornar esse problema.

Módulo e Encapsulamento: Normalmente, o que você considera como um módulo ou componente não é uma única classe, mas alguns tipos altamente coesos. Para fins de encapsulamento, um padrão era aninhar detalhes de implementação privada dentro de classes e tipos aninhados privados. Esse escopo file torna esse padrão de encapsulamento mais limpo.

Testes : Muitas vezes a estrutura de testes é padronizada e cada classe de teste tem sua própria classe DataSet, por exemplo. Aqui também, em vez de declarar classes aninhadas em classes de teste, a restrição file pode ajudar.

No entanto, lembre-se de que uma classe restrita pelo modificador file não será visível nos testes. Elas devem ser consideradas como implementação privada (caixa preta) e devem ser testadas por meio de classes internas ou públicas que as acessam.



E estamos conversados...

"Eu te invoquei, ó Deus, pois me queres ouvir; inclina para mim os teus ouvidos, e escuta as minhas palavras.
Faze maravilhosas as tuas beneficências, ó tu que livras aqueles que em ti confiam dos que se levantam contra a tua destra."

Salmos 17:6,7

Referências:


José Carlos Macoratti