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


19 interfészek

19.1 Általános

Az interfész egy szerződést határoz meg. Az interfészt megvalósító osztálynak vagy szerkezetnek be kell tartania a szerződését. Egy interfész több alapillesztőtől örökölhet, az osztály vagy a szerkezet pedig több interfészt is implementálhat.

Az interfészek a 19.4. §-ban leírtak szerint különböző tagokat tartalmazhatnak. Maga a felület implementációt biztosíthat az általa deklarált függvénytagok egy részének vagy mindegyikének. Azok a tagok, amelyek esetében az interfész nem biztosít implementációt, absztraktak. Az implementációkat az interfészt megvalósító osztályoknak vagy szerkezeteknek kell szolgáltatniuk, vagy olyan származtatott interfésznek kell lenniük, amelyek felülíró definíciót biztosítanak.

Megjegyzés: Korábban egy új függvénytag hozzáadása egy interfészhez hatással volt az adott felülettípus összes meglévő felhasználójára; ez egy törés változás. Az interfészfüggvény tag implementációinak hozzáadása lehetővé tette a fejlesztők számára, hogy frissítsenek egy felületet, miközben a implementátorok továbbra is felülbírálhatják az implementációt. A felület felhasználói nem kompatibilitástörő változásként fogadhatják el a megvalósítást; ha azonban eltérőek a követelmények, felülbírálhatják a megadott implementációkat. végjegyzet

19.2 Interfész deklarációk

19.2.1 Általános

A interface_declaration egy type_declaration (14.7.§), amely új interfésztípust deklarál.

interface_declaration
    : attributes? interface_modifier* 'partial'? 'interface'
      identifier variant_type_parameter_list? interface_base?
      type_parameter_constraints_clause* interface_body ';'?
    ;

A interface_declarationegy választható attribútumkészletből (23. §), majd egy választható interface_modifier-készletből(19.2.2. §), majd egy választható részleges módosítóból (15.2.7. §) áll, amelyet a kulcsszó interface és az interfész nevét tartalmazó azonosító követ, variant_type_parameter_list opcionális specifikáció (19.2.3. §), amelyet egy opcionális interface_base specifikáció követ (19.2.4. §)), majd egy választható type_parameter_constraints_clausespecifikáció (15.2.5.§), majd egy interface_body (19.3. §), amelyet opcionálisan pontosvessző követ.

Egy interfész nyilatkozat nem szolgáltathat type_parameter_constraints_clauses-t, kivéve, ha egy variant_type_parameter_listis megadott.

Egy felületi deklaráció, amely biztosít egy variant_type_parameter_list-et, generikus felületi deklaráció. Ezenkívül az általános osztálydeklarációkba vagy általános szerkezetdeklarációkba ágyazott interfészek maguk is általános felületi deklarációk, mivel a típusargumentumokat a tárolótípus típusargumentumait kell megadni egy beépített típus létrehozásához (8.4. §).

19.2.2 Felületmódosítók

Egy interface_declaration opcionálisan tartalmazhat felületmódosítók sorozatát:

interface_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | unsafe_modifier   // unsafe code support
    ;

unsafe_modifier (24.2. §) csak nem biztonságos kódban érhető el (24. §).

Fordítási időhiba, hogy ugyanaz a módosító többször is megjelenik egy felületi deklarációban.

A new módosító csak az osztályon belül definiált felületeken engedélyezett. Azt határozza meg, hogy a felület elrejti az azonos nevű örökölt elemet, ahogyan az a 15.3.5 szakaszban le van írva.

A public, protected, internalés private módosítók szabályozzák a felület akadálymentességét. Attól függően, hogy milyen környezetben történik az interfész deklarációja, ezen módosítók közül csak néhány engedélyezhető (7.5.2. §). Ha egy részleges típusdeklaráció (§15.2.7) tartalmaz egy hozzáférési specifikációt (a public, protected, internal és private módosítókon keresztül), a §15.2.2 szabályai érvényesek.

19.2.3 Variant type parameter lists

19.2.3.1 Általános

A variánstípus paraméterlistái csak interfész- és delegálási típusok esetén fordulhatnak elő. A különbség a szokásos típusparaméterlista-khez képest az egyes típusparaméterek opcionális variancia annotációja.

variant_type_parameter_list
    : '<' variant_type_parameter (',' variant_type_parameter)* '>'
    ;

variant_type_parameter
    : attributes? variance_annotation? type_parameter
    ;

variance_annotation
    : 'in'
    | 'out'
    ;

Ha a variancia széljegyzete azout, a típusparaméter kovarianciánsnak minősül. Ha a variancia széljegyzete az in, a típusparaméter állítólag contravariant. Ha nincs eltérési széljegyzet, a típusparaméter invariánsnak minősül.

Példa: Az alábbiakban:

interface C<out X, in Y, Z>
{
    X M(Y y);
    Z P { get; set; }
}

X kovariáns, Y kontravariáns és Z invariáns.

záró példa

Ha egy általános interfész több részből áll (15.2.3. §), minden részleges deklarációnak ugyanazt a varianciát kell meghatároznia minden típusparaméterhez.

19.2.3.2 Varianciabiztonság

A variancia-megjegyzések előfordulása egy típus típusparaméter-listájában korlátozza azokat a helyeket, ahol a típusok a típusdeklarációban előfordulhatnak.

A kimenet-biztonságtalan típusú T esetén, ha az alábbiak egyike fennáll:

  • T egy contravariant típusú paraméter
  • T egy olyan tömbtípus, amely kimenet szempontjából nem biztonságos elemtípussal rendelkezik
  • T egy interfész vagy delegáti típus, amely egy általános típusból Sᵢ,... Aₑ lett létrehozvaS<Xᵢ, ... Xₑ>, ahol az alábbi lehetőségek közül legalább egy Aᵢ teljesül:
    • Xᵢ kovariáns vagy invariáns, és Aᵢ nem biztonságos a kimenete.
    • Xᵢ contravariant vagy invariáns, és Aᵢ bemeneti-nem biztonságos.

A T típus bemenete nem biztonságos , ha az alábbiak egyike rendelkezik:

  • T kovariant típusú paraméter
  • T olyan tömbtípus, amely bemeneti-nem biztonságos elemtípussal rendelkezik
  • T egy interfész vagy delegáti típus, amely egy általános típusból S<Aᵢ,... Aₑ> lett létrehozvaS<Xᵢ, ... Xₑ>, ahol az alábbi lehetőségek közül legalább egy Aᵢ teljesül:
    • Xᵢ covariáns vagy invariáns, és Aᵢ bemenet szempontjából nem biztonságos.
    • Xᵢ contravariant vagy invariáns, és Aᵢ kimenete nem biztonságos.

Intuitív módon, a kimeneti pozícióban a kimeneti szempontból nem biztonságos típus tilos, és a bemeneti pozícióban a bemeneti szempontból nem biztonságos típus nem megengedett.

A típus kimenetbiztos, ha nem kimenet-nem biztonságos, és bemenet-biztonságos, ha nem bemeneti-nem biztonságos.

19.2.3.3 Variancia-átalakítás

A varianciajegyzetek célja, hogy megengedőbb (de mégis típusbiztos) átalakításokat biztosítson az interfész- és delegációs típusokra. E célból az implicit (10.2. §) és explicit konverziók (10.3. §) definíciói a variancia-konvertálhatóság fogalmát használják, amely a következőképpen van definiálva:

A típus T<Aᵢ, ..., Aᵥ> variancia-átalakítható típussá T<Bᵢ, ..., Bᵥ> , ha T egy interfész vagy egy delegált típus, amelyet a variánstípus paramétereivel deklaráltak T<Xᵢ, ..., Xᵥ>, és minden egyes változattípus-paraméterhez Xᵢ az alábbiak egyike tartozik:

  • Xᵢ kovariáns, és létezik egy implicit hivatkozás vagy identitásátalakítás Aᵢ-ből Bᵢ-be.
  • Xᵢ kontravariáns, és létezik egy implicit hivatkozás vagy identitás konverzió Bᵢ-ről Aᵢ-re.
  • invariáns, és van egy identitásátalakítás a -ból .

19.2.4 Alapfelületek

Az interfészek örökölhetnek nulla vagy több interfésztípust, amelyeket az interfész explicit alapfelületénekneveznek. Ha egy interfész egy vagy több explicit alapinterfésszel rendelkezik, akkor az interfész-azonosítót kettőspont és vesszővel tagolt alapinterfész-típusok listája következik.

A származtatott interfészek deklarálhatnak olyan új tagokat, amelyek elrejtik az örökölt tagokat (7.7.2.3. §) a bázisfelületeken deklarálva, vagy explicit módon implementálják az örökölt tagokat (19.6.2. §) a bázisfelületeken.

interface_base
    : ':' interface_type_list
    ;

Az explicit alapillesztők felépíthetők interfésztípusok (8.4. §, 19.2. §). Az alapfelület önmagában nem lehet típusparaméter, de magában foglalhatja a hatókörben lévő típusparamétereket is.

A létrehozott interfésztípusok esetében az explicit alapinterfészek úgy jönnek létre, hogy az általános típusdeklarációkon szereplő explicit alapinterfész deklarációit veszik alapul, és az alapinterfész-deklarációban szereplő type_parameter helyére a létrehozott típus megfelelő type_argument-ja kerül.

Az interfész explicit alapillesztőinek legalább olyan akadálymentesnek kell lenniük, mint maga az interfész (7.5.5. §).

Megjegyzés: Például fordítási hibát okoz, ha egy private vagy internal interfészt ad meg egy interface_base elemhez egy public interfészben. végjegyzet

Fordítási idejű hiba, ha egy interfész közvetlenül vagy közvetve örökli saját magától.

Az interfészek alapfelületeiaz explicit alapillesztők és azok alapillesztői. Más szóval, az alapinterfészek készlete az explicit alapinterfészek, azok explicit alapinterfészeinek teljes tranzitív lezárása és így tovább. Az interfész örökli az alapfelületek összes tagját.

Példa: Az alábbi kódban

interface IControl
{
    void Paint();
}

interface ITextBox : IControl
{
    void SetText(string text);
}

interface IListBox : IControl
{
    void SetItems(string[] items);
}

interface IComboBox: ITextBox, IListBox {}

Az alapfelületek a IComboBox, IControl, ITextBox és IListBox. Más szóval, a IComboBox fenti felület örökli a tagokat SetText és SetItems, valamint Paint is.

záró példa

A létrehozott általános típusból öröklő tagok a típushelyettesítés után öröklődnek. Ez azt jelzi, hogy a tag összes összetevőtípusa az alaposztály-deklaráció típusparamétereit a class_base specifikációban használt megfelelő típusargumentumokra cseréli.

Példa: Az alábbi kódban

interface IBase<T>
{
    T[] Combine(T a, T b);
}

interface IDerived : IBase<string[,]>
{
    // Inherited: string[][,] Combine(string[,] a, string[,] b);
}

a felület IDerived a típusparaméter Combine lecserélése után örökli a T metóduststring[,].

záró példa

Az interfészt megvalósító osztály vagy szerkezet implicit módon implementálja az interfész összes alapillesztőjét is.

A részleges interfész deklaráció több részén található interfészek kezeléséről (15.2.7. §) részletesen a 15.2.4.3. fejezet tárgyalja.

Az illesztőfelület minden alapfelületének kimeneti biztonságosnak kell lennie (19.2.3.2. §).

19.3 Interfész törzse

Az interfész interface_body határozza meg a felület tagjait.

interface_body
    : '{' interface_member_declaration* '}'
    ;

19.4 Interfésztagok

19.4.1 Általános

Az interfészek tagjai az alapfelületektől örökölt tagok, és a felület által deklarált tagok.

interface_member_declaration
    : constant_declaration
    | field_declaration
    | method_declaration
    | property_declaration
    | event_declaration
    | indexer_declaration
    | static_constructor_declaration
    | operator_declaration
    | type_declaration
    ;

Ez a záradék kibővíti az osztályok tagjainak leírását (15.3. §) az interfészekre vonatkozó korlátozásokkal. A felülettagok deklarálása member_declarationhasználatával történik a következő további szabályokkal:

  • A finalizer_declaration nem engedélyezett.
  • A példánykonstruktorok ( constructor_declarations) nem engedélyezettek.
  • Minden felületi tag implicit módon rendelkezik nyilvános hozzáféréssel; azonban a statikus konstruktorok kivételével explicit hozzáférés-módosító (7.5.2. §) engedélyezett (15.12. §).
  • A abstract módosító a test nélküli interfészfüggvény-tagokra utal; ez a módosító explicit módon adható meg.
  • Az a felületpéldány-függvénytag, amelynek deklarációja tartalmaz egy törzset, implicit tagvirtual, kivéve, ha a módosító vagy sealed a private módosító van használatban. A virtual módosító explicit módon adható meg.
  • Az private interfész egy vagy sealed több tagjának rendelkeznie kell egy szervvel.
  • A private függvénytagok nem rendelkeznek a módosítóval sealed.
  • A származtatott felület felülbírálhatja az alapillesztőben deklarált absztrakt vagy virtuális tagot.
  • A kifejezetten megvalósított függvénytagok nem rendelkeznek a módosítóval sealed.

Egyes deklarációk, például a constant_declaration (15.4. §) nem korlátozzák az interfészeket.

Az interfész öröklött tagjai kifejezetten nem részei az interfész deklarációs terének. Így a felület lehetővé teszi, hogy deklaráljon egy olyan tagot, amelynek neve vagy aláírása megegyezik az örökölt tag nevével vagy aláírásával. Ha ez történik, a származtatott illesztőtag azt mondja, hogy elrejtse az alap illesztőtagot. Az örökölt tag elrejtése nem minősül hibának, de figyelmeztetést eredményez (7.7.2.3. §).

Ha egy new módosító szerepel egy olyan deklarációban, amely nem rejt el öröklött tagot, a rendszer ennek megfelelően figyelmeztetést ad ki.

Megjegyzés: Az osztály object tagjai szigorúan nem tagjai semmilyen felületnek (19.4. §). Az osztály object tagjai azonban bármely felülettípusban elérhetőek a tagok keresésével (12.5. §). végjegyzet

A többrészesen deklarált felület tagjainak halmaza (15.2.7. §) az egyes részekben deklarált tagok egysége. Az interfész-nyilatkozat minden részének szervei azonos deklarációs térrel (7.3. §) osztoznak, és az egyes tagok hatóköre (7.7. §) az összes rész testületére kiterjed.

Példa: Fontolja meg egy tag és egy IA tulajdonság Mimplementációját tartalmazó felületetP. A implementálási típus C nem biztosít implementációt egyikhez semMP. Azokat olyan hivatkozáson keresztül kell elérni, amelynek fordítási ideje olyan felület, amely implicit módon átalakítható IAIBa vagy a . Ezek a tagok nem találhatók meg a tagkeresés során egy változó típusú Cváltozón.

interface IA
{
    public int P { get { return 10; } }
    public void M()
    {
        Console.WriteLine("IA.M");
    }
}

interface IB : IA
{
    public new int P { get { return 20; } }
    void IA.M()
    {
        Console.WriteLine("IB.M");
    }
}

class C : IB { }

class Test
{
    public static void Main()
    {
        C c = new C();
        ((IA)c).M();                               // cast needed
        Console.WriteLine($"IA.P = {((IA)c).P}");  // cast needed
        Console.WriteLine($"IB.P = {((IB)c).P}");  // cast needed
    }
}

A felületeken IAIBbelül a tag M közvetlenül név szerint érhető el. A metóduson Mainbelül azonban nem tudunk írni c.M() vagy c.P, mivel ezek a nevek nem láthatók. A keresésükhöz a megfelelő felülettípusra kell leadni őket. A deklaráció explicit M felület implementálási IB szintaxist használ. Erre azért van szükség, hogy ez a metódus felülbírálja az egyiket; IAa módosító override nem alkalmazható egy függvénytagra. záró példa

19.4.2 Interfészmezők

Ez a záradék kibővíti a felületeken deklarált mezők §15.5 osztályában lévő mezők leírását.

A felületmezők deklarálása field_declarationhasználatával történik (15.5.1. §) a következő további szabályokkal:

  • A példánymező deklarálása field_declaration fordítási idő hibája.

Példa: A következő program különböző típusú statikus tagokat tartalmaz:

public interface IX
{
    public const int Constant = 100;
    protected static int field;

    static IX()
    {
        Console.WriteLine("static members initialized");
        Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}");
        field = 50;
        Console.WriteLine("static constructor has run");
    }
}

public class Test: IX
{
    public static void Main()
    {
        Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}");
    }
}

A létrehozott kimenet a következő:

static members initialized
constant = 100, field = 0
static constructor has run
constant = 100, field = 50

záró példa

A statikus mezők kiosztásával és inicializálásával kapcsolatos információkért lásd a 19.4.8 .

19.4.3 Felületi módszerek

Ez a záradék a felületeken deklarált metódusok 15.6. osztályában lévő metódusok leírását egészíti ki.

A felületi metódusok method_declarationhasználatával vannak deklarálva (15.6.§)). Az illesztőmetódus-deklaráció attribútumai, return_type, ref_return_type, azonosítója és parameter_list ugyanazzal a jelentéssel rendelkeznek, mint egy osztályban lévő metódusdeklarációé. A felületi metódusok a következő további szabályokkal rendelkeznek:

  • method_modifier nem terjed ki override.

  • Olyan módszer, amelynek törzse kettőspont (;) abstract; a abstract módosító nem szükséges, de engedélyezett.

  • Olyan felületmetódus-deklaráció, amelynek blokktörzse vagy kifejezéstörzse method_body; virtuala virtual módosító nem kötelező, de engedélyezett.

  • A method_declaration csak akkor type_parameter_constraints_clause s, ha type_parameter_list is rendelkezik.

  • Az osztálymetódushoz megadott módosítók érvényes kombinációira vonatkozó követelmények listája az alábbiak szerint bővül:

    • A nem kiirtott statikus deklarációnak blokktesttel vagy kifejezéstörzstel kell rendelkeznie method_body.
    • A nem kiirtott virtuális deklarációnak blokktesttel vagy kifejezéstörzstel kell rendelkeznie method_body.
    • A nem megsemmisített magánbevallásnak method_body kell tartalmaznia egy blokktestet vagy kifejezéstörzset.
    • A nem megsemmisített lezárt nyilatkozatnak method_body kell tartalmaznia egy blokktestet vagy kifejezéstörzset.
    • Az aszinkron deklarációnak blokktörzset vagy kifejezéstörzset kell tartalmaznia method_body.
  • Az interfészmetódus minden paramétertípusának bemenetbiztosnak kell lennie (19.2.3.2. §), és a visszatérési típusnak vagy void kimenetbiztosnak kell lennie.

  • Bármely kimeneti vagy referenciaparaméter-típusnak kimenetbiztosnak kell lennie.

    Megjegyzés: A kimeneti paramétereknek bemenetbiztosnak kell lenniük a gyakori megvalósítási korlátozások miatt. végjegyzet

  • A módszer minden típusparaméterére vonatkozó osztálytípus-korlátozásnak, interfésztípus-korlátozásnak és típusparaméter-kényszernek bemenetbiztosnak kell lennie.

Ezek a szabályok biztosítják, hogy az interfész kovariáns vagy kontravariáns használata típusbiztos maradjon.

Példa:

interface I<out T>
{
    void M<U>() where U : T;     // Error
}

A T típusparaméter-korlátozás a U-en történő használata hibás, mert nem biztonságos bemenetként.

Ha ez a korlátozás nincs érvényben, a típusbiztonságot a következő módon lehet megsérteni:

interface I<out T>
{
    void M<U>() where U : T;
}
class B {}
class D : B {}
class E : B {}
class C : I<D>
{
    public void M<D>() {...} 
}

...

I<B> b = new C();
b.M<E>();

Ez valójában egy hívás C.M<E>-ra. De ez a hívás megköveteli, hogy a E a D-ből származzon, így a típusbiztonság sérülne itt.

záró példa

Megjegyzés: Lásd a 19.4.2. § -t egy olyan példáért, amely nem csak egy implementációval rendelkező statikus metódust jelenít meg, hanem mivel ezt a metódust meghívják Main , és a megfelelő visszatérési típussal és aláírással rendelkezik, az is belépési pont. végjegyzet

Az illesztőben deklarált implementációval rendelkező virtuális metódusokat felül lehet bírálni, hogy egy származtatott felületen absztraktak legyenek. Ezt nevezik újraadási műveletnek.

Példa:

interface IA
{
    void M() { Console.WriteLine("IA.M"); }
}

interface IB: IA
{
    abstract void IA.M();    // reabstraction of M
}

Ez olyan származtatott felületeken hasznos, ahol egy módszer megvalósítása nem megfelelő, és a végrehajtási osztályoknak megfelelőbb megvalósítást kell biztosítaniuk. záró példa

19.4.4 Interfész tulajdonságai

Ez a záradék a felületeken deklarált tulajdonságok 15.7. osztályában lévő tulajdonságok leírását egészíti ki.

A felülettulajdonságok property_declaration(15.7.1. §) használatával deklarálhatók a következő további szabályokkal:

  • property_modifier nem terjed ki override.

  • Az explicit interfésztag-megvalósítás nem tartalmazhat accessor_modifier (15.7.3. §).

  • A származtatott interfészek explicit módon implementálhatnak egy alapfelületen deklarált absztrakt felületi tulajdonságot.

    Megjegyzés: Mivel az illesztő nem tartalmazhat példánymezőket, a felülettulajdonságok nem lehetnek példány automatikus tulajdonságai, mivel ehhez implicit rejtett példánymezők deklarálása szükséges. végjegyzet

  • Az illesztőtulajdonság típusának kimenetbiztosnak kell lennie, ha van beszerelhető tartozék, és bemenetbiztosnak kell lennie, ha van beállítva tartozék.

  • Olyan felületmetódus-deklaráció, amelynek blokktörzse vagy kifejezéstörzse method_body; virtuala virtual módosító nem kötelező, de engedélyezett.

  • Olyan példány property_declaration , amely nem rendelkezik abstractimplementációval; a abstract módosító nem szükséges, de engedélyezett. Soha nem tekinthető automatikusan megvalósított tulajdonságnak (15.7.4. §).

19.4.5 Interfészesemények

Ez a záradék kibővíti a 15.8. osztály eseményeinek leírását a felületeken deklarált események esetében.

Az interfészesemények event_declarationhasználatával vannak deklarálva (15.8.1. §), a következő további szabályokkal:

  • event_modifier nem tartalmazhat.override
  • A származtatott interfész egy alapfelületen deklarált absztrakt interfészeseményt valósíthat meg (15.8.5. §).
  • Fordítási időhiba, hogy egy példányban variable_declaratorsevent_declaration tartalmazzon variable_initializer.
  • A módosítókkal rendelkező virtualsealed példányeseményeknek kiegészítőket kell deklarálniuk. Soha nem tekinthető automatikusan implementált mezőszerű eseménynek (15.8.2. §).
  • A módosítóval rendelkező abstract példányesemények nem deklarálnak tartozékokat.
  • Az interfészesemény típusának bemenetbiztosnak kell lennie.

19.4.6 Felületindexelők

Ez a záradék kibővíti az indexelők leírását a 15.9. osztályban az interfészekben deklarált indexelők esetében.

A felületindexelők deklarálása indexer_declarationhasználatával történik (15.9. §), a következő további szabályokkal:

  • indexer_modifier nem terjed ki override.

  • Olyan indexer_declaration , amely rendelkezik kifejezéstörzsgel , vagy blokktörzset vagy kifejezéstörzset virtualtartalmazó tartozékot tartalmaz; a virtual módosító nem kötelező, de engedélyezett.

  • Olyan indexer_declaration , amelynek kiegészítő teste kettőspont (;) abstract; a abstract módosító nem szükséges, de engedélyezett.

  • Az interfész indexelőjének minden paramétertípusának bemenetbiztosnak kell lennie (19.2.3.2. §).

  • Bármely kimeneti vagy referenciaparaméter-típusnak kimenetbiztosnak kell lennie.

    Megjegyzés: A kimeneti paramétereknek bemenetbiztosnak kell lenniük a gyakori megvalósítási korlátozások miatt. végjegyzet

  • Az interface indexelő típusának kimenetbiztosnak kell lennie, ha van get hozzáférő, és bemenetbiztosnak kell lennie, ha van set hozzáférő.

19.4.7 Interfész operátorok

Ez a záradék kibővíti operator_declaration tagok leírását a 15.10 .

A felület operator_declaration a megvalósítás (19.1. §).

Fordítási időhiba, amikor egy interfész konverziós, egyenlőségi vagy egyenlőtlenségi operátort deklarál.

19.4.8 Interfész statikus konstruktorok

Ez a záradék kibővíti a statikus konstruktorok leírását az interfészekben deklarált statikus konstruktorok 15.12 .

A zárt (8.4.3.3.§)-illesztő statikus konstruktora legfeljebb egyszer fut egy adott alkalmazástartományban. A statikus konstruktor végrehajtását az alábbi műveletek közül az első aktiválja, amely egy alkalmazástartományon belül történik:

  • A rendszer a felület bármely statikus tagjára hivatkozik.
  • Mielőtt a Main metódust meghívná egy olyan interfészre, amely tartalmazza azt a Main metódust (7.1. §), amelyben a végrehajtás megkezdődik.
  • Ez a felület implementációt biztosít egy tag számára, és a végrehajtás az adott tag legspecifikusabb implementációjaként (19.4.10. §) érhető el.

Megjegyzés: Abban az esetben, ha az előző műveletek egyike sem történik meg, az illesztő statikus konstruktora nem hajtható végre olyan program esetében, amelyben az interfészt megvalósító típusok példányai jönnek létre és használhatók. végjegyzet

Egy új zárt felülettípus inicializálásához először létrejön egy új statikus mezőkészlet az adott zárt típushoz. A statikus mezők mindegyike az alapértelmezett értékre van inicializálva. Ezután a statikus mező inicializálói végre lesznek hajtva ezekhez a statikus mezőkhöz. Végül végrehajtja a statikus konstruktort.

Megjegyzés: Lásd a 19.4.2. §-t a különböző típusú statikus tagok (beleértve a főmetódusokat) egy interfészen belül deklarált használatára. végjegyzet

19.4.9 Beágyazott illesztőtípusok

Ez a záradék kibővíti a beágyazott típusok leírását a 15.3.9 osztályban az interfészekben deklarált beágyazott típusok esetében.

Hiba a variance_annotation deklarált típusparaméter hatókörén belül osztálytípust, szerkezettípust vagy számtípust deklarálni (19.2.3.1. §).

Példa: Az alábbi deklaráció C egy hiba.

interface IOuter<out T>
{
    class C { } // error: class declaration within scope of variant type parameter 'T'
}

záró példa

19.4.10 legspecifikusabb megvalósítás

Minden osztálynak és szerkezetnek a legspecifikusabb implementációval kell rendelkeznie minden olyan virtuális tag számára, amelyet az adott típus által implementált összes felületen deklaráltak a típusban vagy annak közvetlen és közvetett interfészeiben megjelenő implementációk között. A legspecifikusabb implementáció egy egyedi implementáció, amely minden más implementációnál pontosabb.

Megjegyzés: A legspecifikusabb végrehajtási szabály biztosítja, hogy a gyémánt felület örökléséből eredő kétértelműséget a programozó explicit módon oldja fel az ütközés helyszínén. végjegyzet

Olyan típus T esetében, amely egy olyan struktúra vagy osztály, amely interfészeket I2 implementál, és I3ahol I2 és I3 mindkettő közvetlenül vagy közvetve származik a tagot Ideklaráló interfészbőlM, a legspecifikusabb megvalósítás M a következő:

  • Ha T a végrehajtást I.Mdeklarálja, az a legspecifikusabb megvalósítás.
  • Ellenkező esetben, ha T egy osztály és egy közvetlen vagy közvetett alaposztály implementációt I.Mdeklarál, akkor a leglevezetettebb alaposztály T a legspecifikusabb implementáció.
  • Ellenkező esetben, ha I2I3 a közvetlen vagy közvetett módon megvalósított T és I3 abból I2 származó interfészek konkrétabb megvalósítást jelentenek, I3.M mint I2.Ma .
  • Ellenkező esetben sem I2.M a konkrétabb, sem I3.M a hiba nem jelentkezik.

Példa:

interface IA
{
    void M() { Console.WriteLine("IA.M"); }
}

interface IB : IA
{
    void IA.M() { Console.WriteLine("IB.M"); }
}

interface IC: IA
{
    void IA.M() { Console.WriteLine("IC.M"); }
}

abstract class C: IB, IC { } // error: no most specific implementation for 'IA.M'

abstract class D: IA, IB, IC // OK
{
    public abstract void M();
}

A legspecifikusabb végrehajtási szabály biztosítja, hogy az ütközést (azaz a gyémánt örökléséből eredő kétértelműséget) a programozó explicit módon oldja meg a konfliktus keletkezésekor. záró példa

19.4.11 Interfésztag-hozzáférés

Az illesztőtagok a taghozzáférés (12.8.7.§) és az indexelőhozzáférés (12.8.12.4. §) kifejezésével I.M érhetők el, és I[A]– ahol I interfésztípus M – az adott interfésztípus állandója, mezője, metódusa, tulajdonsága vagy eseménye, és A indexelő argumentumlista.

A közvetlen vagy közvetett alaposztályú DosztálybanB, ahol B közvetlenül vagy közvetve implementál egy felületetI, és I meghatároz egy metódustM(), a kifejezés base.M() csak akkor érvényes, ha base.M() statikusan (12.3. §) egy osztálytípus implementációjaM().

A szigorúan egyöröklésű felületek (az öröklési lánc minden felülete pontosan nulla vagy egy közvetlen alapfelülettel rendelkezik), a tagkeresés hatása (12.5.§), metódushívás (12.8.1. §) A 0.2) és az indexelő hozzáférés (§12.8.12.4) szabályai pontosan ugyanazok, mint az osztályok és a szerkezetek esetében: A származtatott tagok kevesebb származtatott tagot rejtenek el ugyanazzal a névvel vagy aláírással. A többöröklésű felületek esetében azonban kétértelműség fordulhat elő, ha két vagy több nem kapcsolódó alapfelület azonos nevű vagy aláírású tagokat deklarál. Ez az alcím számos példát mutat be, amelyek közül néhány kétértelműséghez vezet, mások pedig nem. A kétértelműségek megoldásához minden esetben explicit szereplők használhatók.

Példa: Az alábbi kódban

interface IList
{
    int Count { get; set; }
}

interface ICounter
{
    int Count { get; set; }
}

interface IListCounter : IList, ICounter {}

class C
{
    void Test(IListCounter x)
    {
        x.Count = 1;             // Error
        ((IList)x).Count = 1;    // Ok, invokes IList.Count.set
        ((ICounter)x).Count = 1; // Ok, invokes ICounter.Count
    }
}

az első utasítás fordítási időbeli hibát okoz, mert a tag keresése Count-ben (IListCounter) nem egyértelmű. Ahogy a példában látható, a kétértelműséget az x megfelelő alapinterfész típusra történő átalakításával lehet megoldani. Az ilyen kasztoknak nincs futásidejű költségük – csupán abból állnak, hogy fordításkor a példányt kevésbé specifikus típusként kezelik.

záró példa

Példa: Az alábbi kódban

interface IInteger
{
    void Add(int i);
}

interface IDouble
{
    void Add(double d);
}

interface INumber : IInteger, IDouble {}

class C
{
    void Test(INumber n)
    {
        n.Add(1);             // Invokes IInteger.Add
        n.Add(1.0);           // Only IDouble.Add is applicable
        ((IInteger)n).Add(1); // Only IInteger.Add is a candidate
        ((IDouble)n).Add(1);  // Only IDouble.Add is a candidate
    }
}

a n.Add(1) meghívás a túlterhelés feloldási szabályainak alkalmazásával választja ki a IInteger.Add a §12.6.4 szerint. Hasonlóképpen, a hívás n.Add(1.0) kiválasztja IDouble.Add. Explicit leadások beszúrásakor csak egy jelöltmetódus van, így nincs kétértelműség.

záró példa

Példa: Az alábbi kódban

interface IBase
{
    void F(int i);
}

interface ILeft : IBase
{
    new void F(int i);
}

interface IRight : IBase
{
    void G();
}

interface IDerived : ILeft, IRight {}

class A
{
    void Test(IDerived d)
    {
        d.F(1);           // Invokes ILeft.F
        ((IBase)d).F(1);  // Invokes IBase.F
        ((ILeft)d).F(1);  // Invokes ILeft.F
        ((IRight)d).F(1); // Invokes IBase.F
    }
}

a IBase.F tag el van rejtve a ILeft.F tag által. A d.F(1) hívása így ILeft.F-t választja, bár IBase.F úgy tűnik, hogy nincs elrejtve az IRight-en keresztül vezető elérési úton.

A többöröklésű felületeken való elrejtés intuitív szabálya egyszerűen a következő: Ha egy tag bármely elérési útvonalon el van rejtve, az minden elérési útvonalon el van rejtve. Mivel a hozzáférési útvonal IDerived és ILeft között elrejti IBase-t, a tag szintén el van rejtve a hozzáférési útvonalban IBase.F és IDerived között.

záró példa

19.5 A felület minősített tagnevei

Az illesztőtagot néha minősített illesztőtag néven emlegetik. Az interfész tag minősített neve azon interfész neve, amelyben a tag deklarálva van, ezt egy pont követi, majd a tag neve. A tag minősített neve arra a felületre hivatkozik, amelyben a tag deklarálva van.

Példa: Adott deklarációk esetén

interface IControl
{
    void Paint();
}

interface ITextBox : IControl
{
    void SetText(string text);
}

A(z) Paint minősített neve IControl.Paint, és a(z) SetText minősített neve ITextBox.SetText. A fenti példában nem lehet hivatkozni a következőre PaintITextBox.Paint: .

záró példa

Ha egy felület egy névtér része, a minősített illesztőtagok neve magában foglalhatja a névtér nevét.

Példa:

namespace GraphicsLib
{
    interface IPolygon
    {
        void CalculateArea();
    }
}

GraphicsLib névtéren belül mind a IPolygon.CalculateArea, mind a GraphicsLib.IPolygon.CalculateArea a CalculateArea metódushoz tartozó interfész tagnevek.

záró példa

19.6 Interfész implementációi

19.6.1 Általános

A felületeket osztályok és szerkezetek implementálhatják. Annak jelzéséhez, hogy egy osztály vagy strustruktúra közvetlenül implementál egy interfészt, az interfész szerepel az osztály vagy a szerkezet alaposztálylistájában.

Az interfészt C megvalósító osztálynak vagy szerkezetnek I biztosítania vagy örökölnie kell egy implementációt minden olyan tag I számára, aki C hozzáféréssel rendelkezik. A nyilvános tagok I a következő nyilvános tagokban Chatározhatók meg: Az olyan nem nyilvános tagok, amelyekben I elérhetőek C , explicit felületi megvalósítással C határozhatók meg (19.6.2. §).

Egy származtatott típusú tag, amely megfelel az illesztőleképezésnek (19.6.5. §), de nem implementálja az egyező alapfelület-tagot, új tagot vezet be. Ez akkor fordul elő, ha explicit felületi implementációra van szükség a felület tagjának definiálásához.

Példa:

interface ICloneable
{
    object Clone();
}

interface IComparable
{
    int CompareTo(object other);
}

class ListEntry : ICloneable, IComparable
{
    public object Clone() {...}    
    public int CompareTo(object other) {...}
}

záró példa

Az interfészt közvetlenül megvalósító osztály vagy struktúra implicit módon implementálja az interfész összes alapillesztőjét is. Ez akkor is igaz, ha az osztály vagy a szerkezet nem sorolja fel explicit módon az alaposztálylistában szereplő összes alapillesztőt.

Példa:

interface IControl
{
    void Paint();
}

interface ITextBox : IControl
{
    void SetText(string text);
}

class TextBox : ITextBox
{
    public void Paint() {...}
    public void SetText(string text) {...}
}

Itt a TextBox osztály mind a IControl, mind a ITextBox-t megvalósítja.

záró példa

Amikor egy osztály C közvetlenül implementál egy felületet, az összes származtatott C osztály implicit módon is implementálja az interfészt.

Az osztálydeklarációban megadott alapillesztők felépíthetők illesztőtípusok (8.4. §, 19.2. §).

Példa: Az alábbi kód bemutatja, hogyan valósíthat meg egy osztály létrehozott felülettípusokat:

class C<U, V> {}
interface I1<V> {}
class D : C<string, int>, I1<string> {}
class E<T> : C<int, T>, I1<T> {}

záró példa

Az általános osztálybevallás alapfelületeinek meg kell felelniük a 19.6.3.

19.6.2 Explicit felület tag implementációi

Az interfészek megvalósítása céljából az osztály, a szerkezet vagy az interfész explicit felülettag-implementációtdeklarálhat. Az explicit felülettag-implementáció egy metódus, tulajdonság, esemény vagy indexelő deklaráció, amely egy minősített felülettag nevére hivatkozik. A nem nyilvános tagokat az alapillesztőben megvalósító osztálynak vagy szerkezetnek explicit felülettag-implementációt kell deklarálnia. Az alapfelületen tagot implementáló felületnek explicit felülettag-implementációt kell deklarálnia.

A felületleképezésnek megfelelő származtatott illesztőtag (19.6.5. §) elrejti az alap illesztőtagot (7.7.2. §). A fordító figyelmeztetést ad ki, kivéve, ha a new módosító jelen van.

Példa:

interface IList<T>
{
    T[] GetElements();
}

interface IDictionary<K, V>
{
    V this[K key] { get; }
    void Add(K key, V value);
}

class List<T> : IList<T>, IDictionary<int, T>
{
    public T[] GetElements() {...}
    T IDictionary<int, T>.this[int index] {...}
    void IDictionary<int, T>.Add(int index, T value) {...}
}

Itt található a IDictionary<int,T>.this és IDictionary<int,T>.Add explicit felülettag-implementációk.

záró példa

Példa: Bizonyos esetekben előfordulhat, hogy az illesztőtag neve nem felel meg a implementálási osztálynak, ebben az esetben a felülettag explicit felülettag-implementációval implementálható. Egy fájl absztrakciót megvalósító osztály például valószínűleg implementál egy Close tagfüggvényt, amely felszabadítja a fájlerőforrást, és explicit interfész tagfüggvény implementációval valósítja meg az Dispose metódusát az IDisposable interfésznek.

interface IDisposable
{
    void Dispose();
}

class MyFile : IDisposable
{
    void IDisposable.Dispose() => Close();

    public void Close()
    {
        // Do what's necessary to close the file
        System.GC.SuppressFinalize(this);
    }
}

záró példa

Az explicit felülettag-implementáció nem érhető el a megfelelő illesztőtag nevével egy metódushívásban, tulajdonsághozzáférésben, eseményhozzáférésben vagy indexelő hozzáférésben. Az explicit felületpéldányok tag implementációja csak egy felületpéldányon keresztül érhető el, és ebben az esetben egyszerűen a tag neve hivatkozik rá. Az explicit felület statikus tag-implementációja csak a felület nevével érhető el.

Fordítási idejű hiba, ha egy explicit felülettag implementációja bármilyen módosítót tartalmaz az vagy extern kivételével.

Az explicit interfészmetódus implementációja örökli a típusparaméterek korlátozásait az illesztőtől.

Az explicit interfész metódus implementálása során a type_parameter_constraints_clause csak olyan class vagy structprimary_constraintekből állhat, amelyeket a type_parameterekre alkalmaznak, és amelyek az örökölt kényszerek alapján ismertek, hogy vagy hivatkozási, vagy értéktípusok. Az explicit felületi metódus implementációjának aláírásában szereplő űrlap T? bármely típusa, ahol T típusparaméter van, a következőképpen lesz értelmezve:

  • Ha a class típusparaméterhez T kényszert ad hozzá, akkor T? null értékű hivatkozástípus; ellenkező esetben
  • Ha nincs hozzáadott kényszer, vagy egy struct kényszert adnak hozzá, akkor a típusparaméter T egy nullozható értéktípus.

Példa: A következő bemutatja, hogyan működnek a szabályok a típusparaméterek használatakor:

#nullable enable
interface I
{
    void Foo<T>(T? value) where T : class;
    void Foo<T>(T? value) where T : struct;
}

class C : I
{
    void I.Foo<T>(T? value) where T : class { }
    void I.Foo<T>(T? value) where T : struct { }
}

A típusparaméter kényszere where T : classnélkül a referencia típusú típusparaméterrel rendelkező alapmetódus nem bírálható felül. záró példa

Megjegyzés: Az explicit interfész tagok implementációi más hozzáférhetőségi jellemzőkkel rendelkeznek, mint a többi tag. Mivel az explicit felülettag-implementációk soha nem érhetők el egy metódushívásban vagy tulajdonsághozzáférésben lévő minősített illesztőtagnéven keresztül, bizonyos értelemben privátak. Mivel azonban az interfészen keresztül is elérhetők, olyan értelemben is nyilvánosak, mint az a felület, amelyben deklarálva vannak. Az explicit felülettag-implementációk két elsődleges célt szolgálnak:

  • Mivel az explicit illesztőtag-implementációk nem érhetők el osztály- vagy struktúrapéldányokon keresztül, lehetővé teszik, hogy a felületi implementációk ki legyenek zárva egy osztály vagy struktúra nyilvános felületéről. Ez különösen akkor hasznos, ha egy osztály vagy strustruktúra olyan belső felületet implementál, amely nem érdekli az adott osztály vagy szerkezet fogyasztóját.
  • Az explicit felülettag-implementációk lehetővé teszik az azonos aláírással rendelkező felülettagok egyértelműsítését. Explicit felülettag-implementációk nélkül lehetetlen lenne, hogy egy osztály, struktúra vagy felület azonos aláírással és visszatérési típussal rendelkezzen az interfésztagok különböző implementációival, ahogyan az osztály, a szerkezet vagy a felület esetében lehetetlen lenne bármilyen implementáció az azonos aláírású, de eltérő visszatérési típusú felülettagok mindegyikén.

végjegyzet

Ahhoz, hogy az explicit felülettag-implementáció érvényes legyen, az osztálynak, a szerkezetnek vagy az interfésznek el kell neveznie egy interfészt az alaposztályában vagy az alapillesztő-listájában, amely egy olyan tagot tartalmaz, akinek a minősített illesztőtag-neve, típusa, típusparamétereinek száma és paramétertípusai pontosan egyeznek az explicit felülettag-implementációéval. Ha egy interfészfüggvény-tag rendelkezik paramétertömbbel, a társított explicit felülettag-implementáció megfelelő paramétere engedélyezett, de nem kötelező, hogy rendelkezzen a params módosítóval. Ha az interfészfüggvény-tag nem rendelkezik paramétertömbbel, akkor a társított explicit illesztőtag-implementáció nem rendelkezik paramétertömbbel.

Példa: Például a következő osztályban

class Shape : ICloneable
{
    object ICloneable.Clone() {...}
    int IComparable.CompareTo(object other) {...} // invalid
}

A IComparable.CompareTo deklarációja fordítási idejű hibát eredményez, mert IComparable nem szerepel a Shape alaposztályainak listájában, és nem alapinterfésze a ICloneable-nak. Hasonlóképpen, a deklarációkban

class Shape : ICloneable
{
    object ICloneable.Clone() {...}
}

class Ellipse : Shape
{
    object ICloneable.Clone() {...} // invalid
}

ICloneable.Clone deklarálása Ellipse fordítási hibát eredményez, mert ICloneable nincs kifejezetten felsorolva Ellipse alaposztálylistájában.

záró példa

Az explicit interfész tagjának kvalifikált tagneve arra az interfészre hivatkozik, amelyben a tagot deklarálták.

Példa: Így a deklarációkban

interface IControl
{
    void Paint();
}

interface ITextBox : IControl
{
    void SetText(string text);
}

class TextBox : ITextBox
{
    void IControl.Paint() {...}
    void ITextBox.SetText(string text) {...}
}

A Paint explicit felülettag-implementációját IControl.Paint formájában kell írni, nem ITextBox.Paint.

záró példa

19.6.3 A megvalósított interfészek egyedisége

Az általános típusdeklarációval megvalósított interfészeknek minden lehetséges felépített típus esetében egyedinek kell maradniuk. E szabály nélkül lehetetlen lenne meghatározni a megfelelő metódust bizonyos létrehozott típusok meghívásához.

Példa: Tegyük fel, hogy egy általános osztály deklarációja a következőképpen írható:

interface I<T>
{
    void F();
}

class X<U ,V> : I<U>, I<V> // Error: I<U> and I<V> conflict
{
    void I<U>.F() {...}
    void I<V>.F() {...}
}

Ha ez engedélyezve lenne, lehetetlen lenne meghatározni, hogy a következő esetben melyik kódot kell végrehajtani:

I<int> x = new X<int, int>();
x.F();

záró példa

Annak megállapításához, hogy egy általános típusú deklaráció felületlistája érvényes-e, a következő lépéseket kell végrehajtani:

  • Legyen L az általános osztályban, szerkezetben vagy interfészdeklarációban Cközvetlenül megadott interfészek listája.
  • Adja hozzá a L elemekhez a L már meglévő illesztőinek bármely alapillesztőjét.
  • Távolítsa el az ismétlődéseket a L-ból.
  • Ha a típusargumentumok C-be való behelyettesítése után bármely lehetséges konstruált típus két azonos interfészhez vezet L-ben, akkor a L deklarációja érvénytelen. A kényszer deklarációk nem tekinthetők meg az összes lehetséges felépített típus meghatározásakor.

Megjegyzés: A fenti osztálydeklarációban X a felületlista L a következőkből áll l<U> : és I<V>. A deklaráció érvénytelen, mert bármely összefércelés, ahol U és V ugyanaz a típus, ezt a két illesztőfelületet azonos típusokká tenné. végjegyzet

A különböző öröklési szinteken megadott interfészek egyesíthetik az alábbiakat:

interface I<T>
{
    void F();
}

class Base<U> : I<U>
{
    void I<U>.F() {...}
}

class Derived<U, V> : Base<U>, I<V> // Ok
{
    void I<V>.F() {...}
}

Ez a kód akkor is érvényes, ha Derived<U,V> mind I<U>, mind I<V> megvalósít. A kód

I<int> x = new Derived<int, int>();
x.F();

meghívja a metódust Derived, mivel Derived<int,int>' az ténylegesen újra implementálható I<int> (19.6.7. §).

19.6.4 Általános módszerek alkalmazása

Amikor egy általános módszer implicit módon implementál egy interfészmetódust, az egyes metódustípus-paraméterekre vonatkozó korlátozásoknak egyenértékűnek kell lenniük mindkét deklarációban (miután az interfésztípus paramétereit lecserélték a megfelelő típusargumentumokra), ahol a metódustípus paramétereit a balról jobbra lévő sorszámok azonosítják.

Példa: A következő kódban:

interface I<X, Y, Z>
{
    void F<T>(T t) where T : X;
    void G<T>(T t) where T : Y;
    void H<T>(T t) where T : Z;
}

class C : I<object, C, string>
{
    public void F<T>(T t) {...}                  // Ok
    public void G<T>(T t) where T : C {...}      // Ok
    public void H<T>(T t) where T : string {...} // Error
}

a módszer C.F<T> implicit módon implementálI<object,C,string>.F<T>. Ebben az esetben nincs szükség (és nem is engedélyezett) a korlátozás C.F<T> megadására, T: object mivel object az minden típusparaméter implicit kényszere. A metódus C.G<T> implicit módon implementál I<object,C,string>.G<T> , mert a kényszerek megegyeznek az illesztőben lévőkkel, miután az illesztőtípus paramétereit lecserélte a megfelelő típusargumentumokra. A metódus C.H<T> kényszere hiba, mert a lezárt típusok (string ebben az esetben) nem használhatók kényszerként. A kényszer kihagyása szintén hiba lenne, mivel az implicit interfészmetódus implementációinak megkötéseinek meg kell egyeznie. Így lehetetlen implicit módon implementálni I<object,C,string>.H<T>. Ez az interfészmetódus csak explicit felülettag-implementációval implementálható:

class C : I<object, C, string>
{
    ...
    public void H<U>(U u) where U : class {...}

    void I<object, C, string>.H<T>(T t)
    {
        string s = t; // Ok
        H<T>(t);
    }
}

Ebben az esetben az explicit felülettag-implementáció egy olyan nyilvános metódust hív meg, amely szigorúan gyengébb korlátozásokkal rendelkezik. A t-ből s-be való hozzárendelés érvényes, mivel T örökli a kényszert T: string, annak ellenére, hogy ez a kényszer nem fejezhető ki a forráskódban. záró példa

Megjegyzés: Ha egy általános módszer explicit módon implementál egy interfészmetódust, a implementálási módszerre nincs korlátozás (15.7.1. §, 19.6.2. §). végjegyzet

19.6.5 Felületleképezés

Az osztálynak vagy szerkezetnek biztosítania kell az interfészek összes absztrakt tagjának implementálását, amelyek szerepelnek az osztály vagy a szerkezet alaposztálylistájában. Az interfésztagok implementációinak implementálási folyamata egy implementációs osztályban vagy strustruktúra esetében interfészleképezésnek nevezik.

Egy osztály vagy struktúra C interfész-leképezése megkeresi az implementációt az egyes interfészek minden tagjához, amely az alaposztálylistában van megadva C. Egy adott illesztőtag I.Mimplementálását – ahol I a tag M deklarálási felületét is – az egyes osztályok, interfészek vagy szerkezetek Svizsgálatával határozzuk meg, kezdve az egyes egymást követő alaposztályokkal C és annak implementált felületével C, amíg egyezés nem található:

  • Ha a S deklarál egy explicit felülettag-implementációt, amely megfelel a I és M tagoknak, akkor ez a tag a I.M implementációja.
  • Ellenkező esetben, ha a S egy nem statikus nyilvános tag számára tartalmaz deklarációt, amely megfelel a M-nek, akkor ez a tag a I.M megvalósítása. Ha egynél több tag egyezik, nincs meghatározva, hogy melyik tag az implementációja I.M. Ez a helyzet csak akkor fordulhat elő, ha S egy olyan létrehozott típus, amelyben az általános típusban deklarált két tag eltérő aláírással rendelkezik, de a típusargumentumok azonosvá teszik az aláírásukat.

Fordítási időhiba lép fel, ha a implementációk nem találhatók a rendszer alaposztálylistájában megadott összes felület minden tagjára Cvonatkozóan. Az interfész tagjai közé tartoznak azok a tagok, amelyek az alapinterfészekből öröklődnek.

A létrehozott interfésztípus tagjait úgy tekintjük, hogy az egyes típusparamétereket az §15.3.3-ben megadott megfelelő típusargumentumokkal helyettesítik.

Példa: Például az általános felületi deklaráció miatt:

interface I<T>
{
    T F(int x, T[,] y);
    T this[int y] { get; }
}

a létrehozott felület I<string[]> tagjai:

string[] F(int x, string[,][] y);
string[] this[int y] { get; }

záró példa

Felületleképezés céljából egy osztály, interfész vagy strucctag A akkor egyezik meg egy felülettagval B , ha:

  • A és B metódusok, és A és B neve, típusa és paraméterlistája azonos.
  • A és B tulajdonságok, a név és a típus A , és B azonosak, és A ugyanazokkal a tartozékokkal rendelkezik, mint B (A megengedett, hogy további tartozékokkal rendelkezzen, ha nem explicit felületi tag implementáció).
  • A és B események, és a A és B neve és típusa azonos.
  • A és B indexelők, a A és B típus- és paraméterlistái azonosak, és A ugyanazokkal a hozzáférési metódusokkal rendelkezik, mint B (A további hozzáférési metódusokkal is rendelkezhet, ha nem explicit interfész-tag implementáció).

Az interfész-leképezési algoritmus jelentős következményei a következők:

  • Az explicit felülettag-implementációk elsőbbséget élveznek az ugyanabban az osztályban vagy szerkezetben lévő többi taggal szemben az illesztőtagot megvalósító osztály vagy tagstruktúra meghatározásakor.
  • Sem a nem nyilvános, sem a statikus tagok nem vesznek részt a felületleképezésben.

Példa: Az alábbi kódban

interface ICloneable
{
    object Clone();
}

class C : ICloneable
{
    object ICloneable.Clone() {...}
    public object Clone() {...}
}

A(z) ICloneable.Clone tagja a(z) C-ban a(z) Clone megvalósításává válik a(z) ICloneable-ban, mivel az explicit felülettag-implementációk elsőbbséget élveznek más tagokkal szemben.

záró példa

Ha egy osztály vagy szerkezet két vagy több olyan illesztőt implementál, amely azonos nevű, típusú és paramétertípusú tagot tartalmaz, az illesztőtagok mindegyike megfeleltethető egyetlen osztályra vagy strukturált tagra.

Példa:

interface IControl
{
    void Paint();
}

interface IForm
{
    void Paint();
}

class Page : IControl, IForm
{
    public void Paint() {...}
}

Itt mind a Paint, mind a IControl módszerei a IForm metódushoz lettek megfeleltetve a Paint-ben. Természetesen külön explicit felülettag-implementációk is lehetségesek a két módszerhez.

záró példa

Ha egy osztály vagy strustruktúra rejtett tagokat tartalmazó felületet implementál, akkor előfordulhat, hogy egyes tagokat explicit felületi tag-implementációkkal kell implementálni.

Példa:

interface IBase
{
    int P { get; }
}

interface IDerived : IBase
{
    new int P();
}

Ennek az interfésznek a megvalósítása legalább egy explicit felületi tag implementálását igényelné, és az alábbi űrlapok egyikét venné igénybe

class C1 : IDerived
{
    int IBase.P { get; }
    int IDerived.P() {...}
}
class C2 : IDerived
{
    public int P { get; }
    int IDerived.P() {...}
}
class C3 : IDerived
{
    int IBase.P { get; }
    public int P() {...}
}

záró példa

Ha egy osztály több, azonos alapfelülettel rendelkező adaptert implementál, az alapillesztőnek csak egy implementációja lehet.

Példa: Az alábbi kódban

interface IControl
{
    void Paint();
}

interface ITextBox : IControl
{
    void SetText(string text);
}

interface IListBox : IControl
{
    void SetItems(string[] items);
}

class ComboBox : IControl, ITextBox, IListBox
{
    void IControl.Paint() {...}
    void ITextBox.SetText(string text) {...}
    void IListBox.SetItems(string[] items) {...}
}

nem lehet külön implementációkat létrehozni az IControl alaposztálylistában szereplő névhez, az IControl által örökölt ITextBox, és az IControl által örökölt IListBox. Ezen interfészek esetében nincs külön identitás fogalma. Ehelyett a ITextBox és a IListBox ugyanazt a IControl implementációt osztja meg, és a ComboBox egyszerűen úgy tekinthető, mint amely három interfészt valósít meg: IControl, ITextBox, és IListBox.

záró példa

Az alaposztály tagjai részt vesznek a felületleképezésben.

Példa: Az alábbi kódban

interface Interface1
{
    void F();
}

class Class1
{
    public void F() {}
    public void G() {}
}

class Class2 : Class1, Interface1
{
    public new void G() {}
}

A F módszert a Class1 a Class2's rendszer implementációjában használják Interface1.

záró példa

19.6.6 Interfész implementálásának öröklése

Az osztály örökli az alaposztályai által biztosított összes felületi implementációt.

Az interfész explicit újra implementálása nélkül a származtatott osztály semmilyen módon nem módosíthatja az alaposztályoktól örökölt interfész-leképezéseket.

Példa: A deklarációkban

interface IControl
{
    void Paint();
}

class Control : IControl
{
    public void Paint() {...}
}

class TextBox : Control
{
    public new void Paint() {...}
}

a Paint metódus a TextBox-ban elrejti a Paint metódust a Control-ban, de nem módosítja a Control.Paint leképezését az IControl.Paint-re, és az osztálypéldányokon és felületpéldányokon keresztüli Paint hívások a következő hatásokkal járnak

Control c = new Control();
TextBox t = new TextBox();
IControl ic = c;
IControl it = t;
c.Paint();  // invokes Control.Paint();
t.Paint();  // invokes TextBox.Paint();
ic.Paint(); // invokes Control.Paint();
it.Paint(); // invokes Control.Paint();

záró példa

Ha azonban egy interfészmetódus egy osztály virtuális metódusára van leképezve, lehetséges, hogy a származtatott osztályok felülbírálják a virtuális metódust, és módosítják a felület implementációját.

Példa: A fenti deklarációk újraírása a következőre:

interface IControl
{
    void Paint();
}

class Control : IControl
{
    public virtual void Paint() {...}
}

class TextBox : Control
{
    public override void Paint() {...}
}

a következő hatások figyelhetők meg

Control c = new Control();
TextBox t = new TextBox();
IControl ic = c;
IControl it = t;
c.Paint();  // invokes Control.Paint();
t.Paint();  // invokes TextBox.Paint();
ic.Paint(); // invokes Control.Paint();
it.Paint(); // invokes TextBox.Paint();

záró példa

Mivel az explicit felülettag-implementációk nem deklarálhatók virtuálisan, nem lehet felülbírálni egy explicit felülettag-implementációt. Az explicit felülettag implementációja azonban teljesen érvényes arra, hogy meghívjon egy másik metódust, és ezt a másik metódust lehet virtuálisan deklarálni, hogy a származtatott osztályok felüldefiniálhassák azt.

Példa:

interface IControl
{
    void Paint();
}

class Control : IControl
{
    void IControl.Paint() { PaintControl(); }
    protected virtual void PaintControl() {...}
}

class TextBox : Control
{
    protected override void PaintControl() {...}
}

Itt a Control osztályokból származtatott osztályok a IControl.Paint metódus felülbírálásával specializálhatják a PaintControl végrehajtását.

záró példa

19.6.7 Interfész újra implementálása

Az interfész implementálását öröklő osztály újra implementálhatja a felületet úgy, hogy felveszi az alaposztálylistába.

Az interfészek újra implementálása pontosan ugyanazokat a felületleképezési szabályokat követi, mint egy felület kezdeti implementációja. Így az örökölt felületleképezés semmilyen hatással nincs a felület újra implementálására létrehozott felületleképezésre.

Példa: A deklarációkban

interface IControl
{
    void Paint();
}

class Control : IControl
{
    void IControl.Paint() {...}
}

class MyControl : Control, IControl
{
    public void Paint() {}
}

az a tény, hogy Control-t IControl.Paint-re leképezi Control.IControl.Paint-t, nem befolyásolja MyControl újramegvalósítását, ami IControl.Paint-t MyControl.Paint-re leképez.

záró példa

Az örökölt nyilvános tag-deklarációk és az örökölt explicit felülettag-deklarációk részt vesznek az újra implementált felületek felületleképezési folyamatában.

Példa:

interface IMethods
{
    void F();
    void G();
    void H();
    void I();
}

class Base : IMethods
{
    void IMethods.F() {}
    void IMethods.G() {}
    public void H() {}
    public void I() {}
}

class Derived : Base, IMethods
{
    public void F() {}
    void IMethods.H() {}
}

Itt az implementáció leképezi az interfész metódusait IMethodsDerived az Derived.F, Base.IMethods.G, Derived.IMethods.H és Base.I elemekre.

záró példa

Amikor egy osztály implementál egy interfészt, implicit módon az interfész összes alapfelületét is implementálja. Hasonlóképpen, az interfész újra implementálása implicit módon az interfész összes alapfelületének újra implementálása is.

Példa:

interface IBase
{
    void F();
}

interface IDerived : IBase
{
    void G();
}

class C : IDerived
{
    void IBase.F() {...}
    void IDerived.G() {...}
}

class D : C, IDerived
{
    public void F() {...}
    public void G() {...}
}

Itt a IDerived újraimplementálása is újraimplementálja a IBase-t, hozzárendelve a IBase.F-t a D.F-hoz.

záró példa

19.6.8 Absztrakt osztályok és felületek

A nem absztrakt osztályhoz hasonlóan az absztrakt osztálynak is biztosítania kell az osztály alaposztálylistájában felsorolt interfészek összes absztrakt tagjának implementálását. Az absztrakt osztályok azonban interfész metódusokat képezhetnek le absztrakt metódusokra.

Példa:

interface IMethods
{
    void F();
    void G();
}

abstract class C : IMethods
{
    public abstract void F();
    public abstract void G();
    }

Itt a IMethods megvalósítása a F és G elemeket absztrakt metódusokra képezi le, amelyeket felül kell bírálni azokban a nem absztrakt osztályokban, amelyek a C-ből származnak.

záró példa

Az explicit felülettag-implementációk nem lehetnek absztraktak, de az explicit felülettag-implementációk természetesen absztrakt metódusokat is meghívhatnak.

Példa:

interface IMethods
{
    void F();
    void G();
}

abstract class C: IMethods
{
    void IMethods.F() { FF(); }
    void IMethods.G() { GG(); }
    protected abstract void FF();
    protected abstract void GG();
}

Itt a C-ből származó nem absztrakt osztályoknak felül kell bírálniuk a FF-t és a GG-t, így biztosítva ezzel a IMethods tényleges megvalósítását.

záró példa