Megosztás a következőn keresztül:


Részleges események és konstruktorok

Megjegyzés:

Ez a cikk egy funkcióspecifikáció. A specifikáció a funkció tervezési dokumentumaként szolgál. Tartalmazza a specifikáció javasolt módosításait, valamint a funkció tervezése és fejlesztése során szükséges információkat. Ezeket a cikkeket mindaddig közzéteszik, amíg a javasolt specifikációmódosításokat nem véglegesítik, és be nem építik a jelenlegi ECMA-specifikációba.

A szolgáltatás specifikációja és a befejezett implementáció között eltérések lehetnek. Ezeket a különbségeket a vonatkozó nyelvi tervezési értekezlet (LDM) megjegyzései rögzítik.

A funkcióspektusok C# nyelvi szabványba való bevezetésének folyamatáról a specifikációkcímű cikkben olvashat bővebben.

A bajnokkal kapcsolatos kérdés: https://github.com/dotnet/csharplang/issues/9058

Összefoglalás

Engedélyezze az partial módosítót az események és konstruktorok esetén, hogy elválassza a deklarációs és implementációs részeket, hasonlóan a részleges metódusokhoz és részleges tulajdonságok/indexerek.

partial class C
{
    partial C(int x, string y);
    partial event Action<int, string> MyEvent;
}

partial class C
{
    partial C(int x, string y) { }
    partial event Action<int, string> MyEvent
    {
        add { }
        remove { }
    }
}

Motiváció

A C# már támogatja a részleges metódusokat, tulajdonságokat és indexelőket. Hiányoznak a részleges események és konstruktőrök.

A részleges események hasznosak lennének a gyenge esemény könyvtárak számára, ahol a felhasználó definiálhatna:

partial class C
{
    [WeakEvent]
    partial event Action<int, string> MyEvent;

    void M()
    {
        RaiseMyEvent(0, "a");
    }
}

És egy könyvtár által biztosított forrásgenerátor biztosítaná a megvalósításokat.

partial class C
{
    private readonly WeakEvent _myEvent;

    partial event Action<int, string> MyEvent
    {
        add { _myEvent.Add(value); }
        remove { _myEvent.Remove(value); }
    }

    protected void RaiseMyEvent(int x, string y)
    {
        _myEvent.Invoke(x, y);
    }
}

A részleges események és a részleges konstruktorok szintén hasznosak lennének az interop kód generálásában, például a Xamarin esetében, ahol a felhasználó részleges konstruktor és esemény definíciókat írhatna.

partial class AVAudioCompressedBuffer : AVAudioBuffer
{
    [Export("initWithFormat:packetCapacity:")]
    public partial AVAudioCompressedBuffer(AVAudioFormat format, uint packetCapacity);

    [Export("create:")]
    public partial event EventHandler Created;
}

És a forráskód-generátor létrehozná a kötéseket (ebben az esetben Objective-C-hez).

partial class AVAudioCompressedBuffer : AVAudioBuffer
{
    [BindingImpl(BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
    public partial AVAudioCompressedBuffer(AVAudioFormat format, uint packetCapacity) : base(NSObjectFlag.Empty)
    {
        // Call Objective-C runtime:
        InitializeHandle(
            global::ObjCRuntime.NativeHandle_objc_msgSendSuper_NativeHandle_UInt32(
                this.SuperHandle,
                Selector.GetHandle("initWithFormat:packetCapacity:"),
                format.GetNonNullHandle(nameof(format)),
                packetCapacity),
            "initWithFormat:packetCapacity:");
    }

    public partial event EventHandler Created
    {
        add { /* ... */ }
        remove { /* ... */ }
    }
}

Részletes kialakítás

Általános

Az esemény deklaráció szintaxisa (§15.8.1) kibővül, hogy lehetővé tegye a partial módosítót.

 event_declaration
-    : attributes? event_modifier* 'event' type variable_declarators ';'
+    : attributes? event_modifier* 'partial'? 'event' type variable_declarators ';'
-    | attributes? event_modifier* 'event' type member_name
+    | attributes? event_modifier* 'partial'? 'event' type member_name
         '{' event_accessor_declarations '}'
     ;

Az példánykészítő deklaráció szintaxisa (§15.11.1) ki van terjesztve, hogy lehetővé tegye a partial módosítót:

 constructor_declaration
-    : attributes? constructor_modifier* constructor_declarator constructor_body
+    : attributes? constructor_modifier* 'partial'? constructor_declarator constructor_body
     ;

Ne feledje, hogy van egy javaslat arra, hogy az partial módosítót a módosítók között bárhova el lehessen helyezni, és ne csak az utolsó helyen (ugyanez vonatkozik az eljárások, tulajdonságok és típusdeklarációk esetében is).

Az partial módosítóval ellátott eseménydeklarációt részleges eseménydeklarációnak nevezik, és összekapcsolják egy vagy több részleges eseménnyel a megadott nevekkel (vegye figyelembe, hogy egy eseménydeklaráció hozzáférők nélkül több eseményt is megadhat).

A partial ellátott konstruktor deklarációt részleges konstruktor deklarációnak nevezik, és a meghatározott aláírású részleges konstruktorhoz társul.

Egy részleges esemény deklarációról azt mondják, hogy egy implementáló deklaráció, ha meghatározza a event_accessor_declarations, vagy ha rendelkezik a extern módosítóval. Egyébként ez egy meghatározó deklaráció.

Egy részleges konstruktor deklarációt akkor nevezünk meghatározó deklarációnak, ha szemiológ ponttal záródó törzse van, és hiányzik belőle a extern módosító. Ellenkező esetben ez egy végrehajtási nyilatkozat.

partial class C
{
    // defining declarations
    partial C();
    partial C(int x);
    partial event Action E, F;

    // implementing declarations
    partial C() { }
    partial C(int x) { }
    partial event Action E { add { } remove { } }
    partial event Action F { add { } remove { } }
}

Csak a részleges tag meghatározó deklarációja vesz részt a keresésben, és azt használati helyeken, valamint a metaadat kibocsátásakor veszik figyelembe. (Kivéve az alább részletezett dokumentációs megjegyzéseket.) A megvalósító deklarációs aláírás használatos a kapcsolódó törzsek nullázhatósági elemzésére.

Egy részleges esemény vagy konstruktor:

  • Csak egy részleges típus tagjaként nyilvánítható ki.
  • Kell lennie egy meghatározó és egy megvalósító deklarációnak.
  • Nem engedélyezett, hogy a abstract módosító legyen.
  • Nem lehet kifejezetten megvalósítani egy interfész tagját.

A részleges esemény nem mezőszerű (§15.8.2), azaz:

  • Nem rendelkezik háttértárral vagy a fordító által generált elérési módszerekkel.
  • Csak += és -= műveletekben használható, értékként nem.

A meghatározó részleges konstruktor deklarációnak nem lehet konstruktor inicializálója (: this() vagy : base(); §15.11.2).

Elemzési megszakítás

A partial módosító több kontextusban való engedélyezése törést okozó változás:

class C
{
    partial F() => new partial(); // previously a method, now a constructor
    @partial F() => new partial(); // workaround to keep it a method
}

class partial { }

A nyelvi elemző egyszerűsítése érdekében a partial módosító bármilyen módszer-szerű deklarációnál elfogadott (pl. helyi függvények és felső szintű szkript metódusok is), még akkor is, ha nem részletezzük a nyelvtani változtatásokat fent.

Tulajdonságok

A keletkező esemény vagy konstruktor attribútumai az adott pozíciókban lévő részleges deklarációk egyesített attribútumai. A kombinált attribútumok nem meghatározott sorrendben vannak összefűzve, és a duplikátumok nincsenek eltávolítva.

A methodattribute_target (§22.3) figyelmen kívül van hagyva részleges eseménydeklarációk esetében. A hozzáférési attribútumokat csak a hozzáférési deklarációkból lehet használni (amelyek csak a megvalósító deklaráció alatt lehetnek jelen). Ne feledje, hogy a param és a returnattribute_target már figyelmen kívül van hagyva minden eseménydeklaráció esetén.

A hívó-info attribútumokat az implementációs deklaráció során a fordító figyelmen kívül hagyja, ahogy azt a szakaszban leírt részleges tulajdonság-javaslat előírja. Kérjük, vegye figyelembe, hogy ez az összes részleges tagra vonatkozik, beleértve a részleges eseményeket és konstruktőröket is.

Aláírások

Egy részleges tag mindkét deklarációjának egyező aláírással kell rendelkeznie hasonlóan, mint a részleges tulajdonságok:

  1. A részleges deklarációk közötti típus- és referenciafajta-különbségek, amelyek jelentősek a futási eredmény szempontjából, fordítási hibát okoznak.
  2. A párhuzamos deklarációk között a tuple elemek nevének eltérései fordítási időben hibát eredményeznek.
  3. A deklarációknak ugyanazokkal a módosítókkal kell rendelkezniük, bár a módosítók más sorrendben is megjelenhetnek.
    • Kivétel: ez nem vonatkozik a extern módosítóra, amely csak a végrehajtási deklarációban jelenhet meg.
  4. Minden más szintaktikai különbség a részleges deklarációk aláírásában fordítási időben figyelmeztetést eredményez, a következő kivételekkel:
    • Az attribútumlistáknak nem kell egyezniük, ahogyan azt fent leírtuk.
    • A nullálható kontextuskülönbségei (mint például gondatlan vs. annotált) nem okoznak figyelmeztetéseket.
    • A alapértelmezett paraméterértékeknek nem kell egyezniük, de figyelmeztetést adunk ki, ha a végrehajtó konstruktor deklarációjának alapértelmezett paraméterértékei vannak (mert ezeket figyelmen kívül hagynák, mivel csak a meghatározó deklaráció vesz részt a keresésben).
  5. Figyelmeztetés történik, ha a paraméternevek különböznek a deklarációkat definiáló és implementáló konstruktorok között.
  6. Az olyan nullabilitási különbségek, amelyek nem járnak a feledésbe vonással, figyelmeztetéseket eredményeznek.

Dokumentációs megjegyzések

Megengedett, hogy mind a definiáló, mind a megvalósító deklarációba dok kommentárokat illesszünk. Vegye figyelembe, hogy a dokumentációs megjegyzések nem támogatottak az esemény-hozzáférési eljárásoknál.

Amikor dokumentációs megjegyzések csak egy részleges tag deklarációjánál vannak jelen, azok a dokumentációs megjegyzések normál módon kerülnek felhasználásra (kirajzolódnak a Roslyn API-kon keresztül, kiadásra kerülnek a dokumentációs XML fájlba).

Ha egy részleges tag mindkét deklarációjánál jelen vannak a dokumentációs megjegyzések, az összes dokumentációs megjegyzést, amely a meghatározó deklaráción található, elvetjük, és csak a végrehajtó deklaráción található dokumentációs megjegyzéseket használjuk.

Amikor a paraméternevek eltérnek egy részleges tag nyilatkozatai között, a paramref elemek a forráskódban található dokumentációs megjegyzéshez társított deklarációból származó paraméterneveket használják. Például egy, egy dokumentum megjegyzésben elhelyezett paramref, amely egy megvalósító deklaráción helyezkedik el, a megvalósító deklaráció paraméter szimbólumaira hivatkozik azok paraméter nevein keresztül. Ez zavaró lehet, mert a metaadat aláírás a meghatározó deklarációban található paraméterneveket fogja használni. Javasolt biztosítani, hogy a paraméternevek megegyezzenek a részleges tag deklarációi között, hogy elkerüljük a zavart.

Kérdések megnyitása

Tagtípusok

Akarunk részleges eseményeket, konstruktorokat, operátorokat, mezőket? Az első két tagtípust javasoljuk, de bármelyik másik részhalmaz is megfontolható.

Részleges elsődleges konstruktorokat is figyelembe lehetne venni, például lehetővé téve a felhasználó számára, hogy ugyanazt a paraméterlistát használja több részleges típusdeklarációnál.

Attribútum helyek

Fel kell ismernünk a [method:] attribútum célmegjelölőt részleges eseményekhez (vagy csak a meghatározó nyilatkozatokhoz)? Az eredményül kapott accessor attribútumok a method célzású attribútumok összefűzéséből állnak mindkét (vagy csak a meghatározó) deklarációs részből, illetve az öncélzó és method célzású attribútumokból az implementáló deklaráció accessor-jaiból. Az attribútumok kombinálása különböző deklarációs típusokból példátlan lenne, és valóban, a Roslyn attribútum-illesztési jelenlegi megvalósítása ezt nem támogatja.

Fontolóra vehetjük a [param:] és a [return:] felismerését nemcsak részleges eseményeknél, hanem minden mezőszerű és külső eseménynél is. További részletekért lásd: https://github.com/dotnet/roslyn/issues/77254.