Sdílet prostřednictvím


Místní typy souborů

Poznámka

Tento článek je specifikace funkce. Specifikace slouží jako návrhový dokument pro funkci. Zahrnuje navrhované změny specifikace spolu s informacemi potřebnými při návrhu a vývoji funkce. Tyto články se publikují, dokud nebudou navrhované změny specifikace finalizovány a začleněny do aktuální specifikace ECMA.

Mezi specifikací funkce a dokončenou implementací může docházet k nějakým nesrovnalostem. Tyto rozdíly jsou zachyceny v poznámkách příslušné schůzky návrhu jazyka (LDM).

Další informace o procesu přijetí specifikací funkcí do jazyka C# najdete v článku o specifikacích .

Problém šampiona: https://github.com/dotnet/csharplang/issues/5529

Shrnutí

Povolte modifikátor file u deklarací typů nejvyšší úrovně. Typ existuje pouze v souboru, kde je deklarován.

// 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.

Motivace

Naší primární motivací jsou generátory zdrojového kódu. Zdrojové generátory fungují přidáním souborů do kompilace uživatele.

  1. Tyto soubory by měly být schopné obsahovat podrobnosti implementace, které jsou skryté od zbytku kompilace, ale jsou použitelné v celém souboru, ve kterém jsou deklarovány.
  2. Chceme snížit potřebu generátorů hledat názvy typů, které nebudou kolidovat s deklaracemi v uživatelském kódu nebo kódu z jiných generátorů.

Podrobný návrh

  • Modifikátor file přidáme do následujících sad modifikátoru:
  • Modifikátor file lze použít pouze v typu nejvyšší úrovně.

Pokud má typ modifikátor file, říká se o něm, že jde o místní typ souboru .

Přístupnost

Modifikátor file není klasifikovaný jako modifikátor přístupnosti. Na typu nelze použít, v kombinaci s file, žádné modifikátory přístupnosti. file se považuje za nezávislý koncept od přístupnosti. Protože místní typy souborů nelze zanořit, lze u typů internal použít pouze výchozí přístupnost file.

public file class C1 { } // error
internal file class C2 { } // error
file class C3 { } // ok

Pojmenování

Implementace zaručuje, že místní typy souborů v různých souborech se stejným názvem se liší od modulu runtime. Přístupnost a název typu v metadatech je definovaný implementací. Záměrem je umožnit kompilátoru přijmout jakékoli budoucí funkce omezení přístupu v modulu runtime, které jsou pro tuto funkci vhodné. Očekává se, že v počáteční implementaci se použije přístupnost internal a vygeneruje se nesrozumitelný název, který závisí na souboru, ve kterém je typ deklarován.

Vyhledat

Změníme oddíl vyhledávání členů následujícím způsobem (nový text v tučným písmem):

  • Pokud je K nula, odeberou se všechny vnořené typy, jejichž deklarace zahrnují parametry typu. Pokud K není nula, odeberou se všechny členy s jiným počtem parametrů typu. Pokud je K nula, metody s parametry typu se neodeberou, protože proces odvozování typu (§11.6.3) může být schopen odvodit argumenty typu.
  • Dále nechte F být kompilační jednotkou, která obsahuje výraz, ve kterém probíhá vyhledávání členů. Všechny členy, které jsou místní typy souborů a nejsou deklarovány v F jsou ze sady odebrány.
  • Další, pokud sada přístupných členů obsahuje místní typy souborů, odeberou se ze sady všechny členy, které nejsou typy místních souborů.

Poznámky

Tato pravidla nepovolují použití místních typů souborů mimo soubor, ve kterém jsou deklarovány.

Tato pravidla také umožňují místnímu typu souboru stínové oboru názvů nebo jiného než místního typu:

// 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
    }
}

Všimněte si, že obory části specifikace neaktualizujeme. Je to proto, že jak uvádí specifikace:

Rozsah názvu je oblast textu programu, ve které je možné odkazovat na entitu deklarovanou názvem bez kvalifikace názvu.

Obor má vliv jenom na vyhledávání nekvalifikovaných názvů. To není úplně správný koncept, který bychom mohli využít, protože musíme také ovlivnit vyhledávání kvalifikovaných názvů:

// 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
    }
}

Proto neurčíme funkci z hlediska rozsahu, ve kterém je typ obsažen, ale jako další "pravidla filtrování" ve vyhledávání členů.

Atributy

Místní třídy souborů mohou být typy atributů a lze je použít jako atributy v rámci místních typů souborů i jiných než souborových místních typů, stejně jako kdyby byl typ atributu jiným než místním typem. Název metadat typu atributu file-local stále prochází stejnou strategií generování názvů jako jiné typy file-local. To znamená, že zjištění přítomnosti místního typu souboru pomocí pevně zakódovaného názvu řetězce bude pravděpodobně nepraktické, protože to vyžaduje v závislosti na interní strategii generování názvů kompilátoru, která se může v průběhu času měnit. Detekce prostřednictvím typeof(MyFileLocalAttribute) ale funguje.

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
    }
}

Použití v podpisech

Obecně je potřeba zabránit tomu, aby se typy lokální pro soubor objevovaly v parametrech člena, návratových hodnotách a omezeních parametrů typu, kde v okamžiku použití člena nemusí být typ lokální pro soubor v dosahu.

Všimněte si, že nesouborové místní typy mají povoleno implementovat místní rozhraní, podobně jako typy mohou implementovat méně přístupná rozhraní. V závislosti na typech, které jsou přítomné ve členech rozhraní, může dojít k porušení pravidel v následující části.

Povolit pouze použití podpisu ve členech místních typů souborů

Nejjednodušší způsob, jak to zajistit, je vynutit, aby se místní typy souborů mohly objevit pouze v podpisech nebo jako základní typy jiných typů souborů:

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
}

Všimněte si, že to omezuje použití v explicitních implementacích, i když jsou takové použití bezpečné. Provedeme to, abychom zjednodušili pravidla pro počáteční iteraci funkce.

file interface I
{
    void M(I i);
}

class C : I
{
    void I.M(I i) { } // error
}

global using static

Jedná se o chybu v době kompilace při použití místního typu souboru v direktivě global using static, tj.

global using static C; // error

file class C
{
    public static void M() { }
}

Implementace/přepsání

Deklarace místních typů souborů mohou implementovat rozhraní, přepsat virtuální metody atd. stejně jako běžné deklarace typu.

file struct Widget : IEquatable<Widget>
{
    public bool Equals(Widget other) => true;
}