Sdílet prostřednictvím


19 Rozhraní

19.1 Obecné

Rozhraní definuje kontrakt. Třída nebo struktura, která implementuje rozhraní, musí dodržovat svou smlouvu. Rozhraní může dědit z více základních rozhraní a třída nebo struktura může implementovat více rozhraní.

Rozhraní mohou obsahovat různé druhy členů, jak je popsáno v §19.4. Samotné rozhraní může poskytnout implementaci některých nebo všech členů funkce, které deklaruje. Členy, pro které rozhraní neposkytuje implementaci, jsou abstraktní. Jejich implementace musí být dodány třídami nebo strukturami, které implementují rozhraní, nebo odvozené rozhraní, které poskytují přepsání definice.

Poznámka: Historicky přidání nového člena funkce do rozhraní ovlivnilo všechny stávající uživatele tohoto typu rozhraní; to byla zásadní změna. Přidání implementací členů funkce rozhraní umožňuje vývojářům upgradovat rozhraní a zároveň umožnit všem implementátorům přepsat tuto implementaci. Uživatelé rozhraní mohou přijmout implementaci jako nerušnou změnu; Pokud se však jejich požadavky liší, mohou přepsat poskytnuté implementace. koncová poznámka

19.2 Deklarace rozhraní

19.2.1 Obecné

Interface_declaration je type_declaration (§14.7), který deklaruje nový typ rozhraní.

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

Interface_declaration se skládá z volitelné sady atributů (§23), po níž následuje volitelná sada interface_modifiers (§19.2.2), za kterou následuje volitelný částečný modifikátor (§15.2.7), za kterým následuje klíčové slovo interface a identifikátor, který pojmenuje rozhraní, po ní následuje volitelná specifikace variant_type_parameter_list (§19.2.3), po níž následuje volitelná specifikace interface_base (§19.2.4)), po níž následuje volitelná specifikace type_parameter_constraints_clause(§15.2.5), za kterou následuje interface_body (§19.3), případně následuje středník.

Prohlášení o rozhraní nesmí poskytovat type_parameter_constraints_clause, pokud současně neposkytuje variant_type_parameter_list.

Deklarace rozhraní, která poskytuje variant_type_parameter_list je obecná deklarace rozhraní. Kromě toho je jakékoli rozhraní vnořené uvnitř obecné deklarace třídy nebo obecná deklarace struktury sama o sobě obecná deklarace rozhraní, protože argumenty typu obsahujícího typu musí být zadány k vytvoření typu konstrukce (§8.4).

19.2.2 Modifikátory rozhraní

Interface_declaration může volitelně obsahovat posloupnost modifikátorů rozhraní:

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

unsafe_modifier (§24.2) je k dispozici pouze v nebezpečném kódu (§24).

Jedná se o chybu v době kompilace, aby se stejný modifikátor zobrazoval vícekrát v deklaraci rozhraní.

new Modifikátor je povolen pouze u rozhraní definovaných v rámci třídy. Určuje, že rozhraní skryje zděděný člen se stejným názvem, jak je popsáno v §15.3.5.

publicModifikátory , , protectedinternala private řídí přístupnost rozhraní. V závislosti na kontextu, ve kterém se deklarace rozhraní vyskytuje, mohou být povoleny pouze některé z těchto modifikátorů (§7.5.2). Pokud prohlášení o částečném typu (§15.2.7) zahrnuje specifikaci přístupnosti (prostřednictvím public, protected, internala private modifikátorů), použijí se pravidla v §15.2.2 .

19.2.3 Seznamy parametrů variantního typu

19.2.3.1 Obecné

Seznamy parametrů variantního typu mohou nastat pouze u typů rozhraní a delegátů. Rozdíl od běžných type_parameter_lists je volitelný variance_annotation u každého parametru typu.

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

variant_type_parameter
    : attributes? variance_annotation? type_parameter
    ;

variance_annotation
    : 'in'
    | 'out'
    ;

Pokud je outrozptylová poznámka , parametr typu se říká, že je kovariantní. Pokud je inrozptylová poznámka , parametr typu se říká, že je kontravariantní. Pokud neexistuje žádná odchylka anotace, parametr typu je invariantní.

Příklad: V následujícím příkladu:

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

X je kovariantní, Y je kontravariantní a Z je invariantní.

konec příkladu

Je-li obecné rozhraní deklarováno ve více částech (§15.2.3), musí každé částečné prohlášení určit stejnou odchylku pro každý parametr typu.

19.2.3.2 Bezpečnost rozptylu

Výskyt poznámek rozptylu v seznamu parametrů typu typu omezuje místa, kde se můžou v deklaraci typu vyskytovat typy.

Typ T je nebezpečný pro výstup, pokud platí jedna z následujících možností:

  • T je parametr kontravariantního typu.
  • T je typ pole s nebezpečným typem výstupního prvku.
  • T je typ Sᵢ,... Aₑ rozhraní nebo delegáta vytvořený z obecného typu S<Xᵢ, ... Xₑ> , kde platí alespoň pro jednu Aᵢ z následujících možností:
    • Xᵢ je kovariantní nebo invariantní a Aᵢ je nebezpečný pro výstup.
    • Xᵢ je kontravariantní nebo invariantní a Aᵢ je nebezpečný.

Typ T je nebezpečný , pokud platí jedna z následujících možností:

  • T je kovariantní parametr typu.
  • T je typ pole se vstupním nebezpečným typem elementu.
  • T je typ S<Aᵢ,... Aₑ> rozhraní nebo delegáta vytvořený z obecného typu S<Xᵢ, ... Xₑ> , kde platí alespoň pro jednu Aᵢ z následujících možností:
    • Xᵢ je kovariantní nebo invariantní a Aᵢ je nebezpečný pro vstup.
    • Xᵢ je kontravariantní nebo invariantní a Aᵢ je nebezpečný pro výstup.

Intuitivně je ve výstupní pozici zakázán nebezpečný typ výstupu a vstupní nebezpečný typ je zakázán ve vstupní pozici.

Typ je bezpečný pro výstup, pokud není nebezpečný pro výstup a je bezpečný pro vstup, pokud není nebezpečný pro vstup.

19.2.3.3 Převod rozptylu

Účelem poznámek odchylek je poskytnout více lenientních (ale přesto bezpečných) převodů na typy rozhraní a delegátů. Za tímto účelem se definice implicitního (§10.2) a explicitního převodu (§10.3) používají pojem variance-convertibility, který je definován takto:

Typ T<Aᵢ, ..., Aᵥ> je variance-konvertibilní na typ T<Bᵢ, ..., Bᵥ> , pokud T je buď rozhraní, nebo delegát typ deklarován s parametry T<Xᵢ, ..., Xᵥ>variant typu , a pro každý parametr Xᵢ typu varianty jeden z následujících blokování:

  • Xᵢ je kovariantní a implicitní odkaz nebo převod identity existuje z Aᵢ do Bᵢ
  • Xᵢ je kontravariantní a implicitní odkaz nebo převod identity existuje z Bᵢ do Aᵢ
  • Xᵢ je invariantní a převod identity existuje z Aᵢ do Bᵢ

19.2.4 Základní rozhraní

Rozhraní může dědit z nuly nebo více typů rozhraní, které se nazývají explicitní základní rozhranírozhraní rozhraní. Pokud má rozhraní jedno nebo více explicitních základních rozhraní, pak v deklaraci tohoto rozhraní následuje identifikátor rozhraní dvojtečka a čárkami oddělený seznam základních typů rozhraní.

Odvozené rozhraní může deklarovat nové členy, které skrývají zděděné členy (§7.7.2.3) deklarované v základních rozhraních nebo explicitně implementují zděděné členy (§19.6.2) deklarované v základních rozhraních.

interface_base
    : ':' interface_type_list
    ;

Explicitní základní rozhraní lze vytvořit typy rozhraní (§8.4, §19.2). Základní rozhraní nemůže být parametr typu sám, i když může zahrnovat parametry typu, které jsou v oboru.

U typu vytvořeného rozhraní jsou explicitní základní rozhraní tvořena explicitními deklaracemi základního rozhraní pro deklaraci obecného typu a nahrazením každé type_parameter v deklaraci základního rozhraní odpovídající type_argument konstruovaného typu.

Explicitní základní rozhraní rozhraní musí být alespoň tak přístupné jako samotné rozhraní (§7.5.5).

Poznámka: Jedná se například o chybu v době kompilace, která určuje private rozhraní internal v interface_basepublic rozhraní. koncová poznámka

Jedná se o chybu v době kompilace, která rozhraní přímo nebo nepřímo dědí ze sebe.

Základní rozhranírozhraní rozhraní jsou explicitní základní rozhraní a jejich základní rozhraní. Jinými slovy, sada základních rozhraní je kompletní tranzitivní uzavření explicitních základních rozhraní, jejich explicitních základních rozhraní atd. Rozhraní dědí všechny členy jeho základních rozhraní.

Příklad: V následujícím kódu

interface IControl
{
    void Paint();
}

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

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

interface IComboBox: ITextBox, IListBox {}

základní rozhraní IComboBox jsou IControl, ITextBoxa IListBox. Jinými slovy, IComboBox výše uvedené rozhraní dědí členy SetText a SetItems také Paint.

konec příkladu

Členové zděděné z vytvořeného obecného typu se dědí po nahrazení typu. To znamená, že všechny typy složek v členu mají parametry typu deklarace základní třídy nahrazeny odpovídajícími argumenty typu použitými ve specifikaci class_base.

Příklad: V následujícím kódu

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

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

rozhraní IDerived dědí metodu Combine po nahrazení parametru Tstring[,]typu .

konec příkladu

Třída nebo struktura, která implementuje rozhraní také implicitně implementuje všechny základní rozhraní rozhraní.

Zpracování rozhraní na více částech částečné deklarace rozhraní (§15.2.7) je dále popsáno v §15.2.4.3.

Každé základní rozhraní rozhraní musí být výstupní-bezpečné (§19.2.3.2).

19.3 Tělo rozhraní

Interface_body rozhraní definuje členy rozhraní.

interface_body
    : '{' interface_member_declaration* '}'
    ;

19.4 Členy rozhraní

19.4.1 Obecné

Členy rozhraní jsou členy zděděné ze základních rozhraní a členy deklarované samotným rozhraním.

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

Tato klauzule rozšiřuje popis členů ve třídách (§15.3) o omezení pro rozhraní. Členy rozhraní jsou deklarovány pomocí member_declarations následujícími dalšími pravidly:

  • Finalizer_declaration není povolený.
  • Konstruktory instancí, constructor_declarations, nejsou povoleny.
  • Všichni členové rozhraní mají implicitně veřejný přístup; je však povolen explicitní modifikátor přístupu (§7.5.2) s výjimkou statických konstruktorů (§15.12).
  • abstract Modifikátor je odvozen pro členy funkce rozhraní bez těl. Tento modifikátor může být výslovně uveden.
  • Člen funkce instance rozhraní, jehož deklarace obsahuje tělo je implicitně virtual člen, pokud sealed není použit nebo private modifikátor. virtual Modifikátor může být explicitně uveden.
  • Člen private rozhraní nebo sealed funkce musí mít subjekt.
  • Člen private funkce nesmí mít modifikátor sealed.
  • Odvozené rozhraní může přepsat abstraktní nebo virtuální člen deklarovaný v základním rozhraní.
  • Explicitně implementovaný člen funkce nesmí mít modifikátor sealed.

Některá prohlášení, například constant_declaration (§15.4), nemají v rozhraních žádná omezení.

Zděděné členy rozhraní nejsou konkrétně součástí prostoru deklarace rozhraní. Rozhraní je tedy povoleno deklarovat člen se stejným názvem nebo podpisem jako zděděný člen. V takovém případě se odvozený člen rozhraní říká, že skryje člen základního rozhraní. Skrytí zděděného člena se nepovažuje za chybu, ale výsledkem je upozornění (§7.7.2.3).

new Pokud je modifikátor součástí deklarace, která neskryje zděděný člen, zobrazí se upozornění.

Poznámka: Členové třídy object nejsou, přísně řečeno, členy jakéhokoli rozhraní (§19.4). Členy třídy object jsou však k dispozici prostřednictvím vyhledávání členů v libovolném typu rozhraní (§12.5). koncová poznámka

Sada členů rozhraní deklarovaných ve více částech (§15.2.7) je sjednocení členů deklarovaných v každé části. Subjekty všech částí prohlášení o rozhraní mají stejný prostor prohlášení (§7.3) a rozsah každého člena (§7.7) se vztahuje na subjekty všech částí.

Příklad: Zvažte rozhraní IA s implementací člena M a vlastnosti P. Implementační typ C neposkytuje implementaci ani MPpro . Musí být přístupné prostřednictvím odkazu, jehož typ kompilace-čas je rozhraní, které je implicitně konvertibilní na IA nebo IB. Tyto členy nejsou nalezeny prostřednictvím vyhledávání členů v proměnné typu C.

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

V rozhraních IA a IBčlen M je přístupný přímo podle názvu. V rámci metody Mainvšak nemůžeme napsat c.M() nebo c.P, protože tyto názvy nejsou viditelné. K jejich nalezení je potřeba přetypovat na příslušný typ rozhraní. Deklarace M in IB používá explicitní syntaxi implementace rozhraní. To je nezbytné k tomu, aby tato metoda přepsala hodnotu v IA; modifikátor override nelze použít u člena funkce. konec příkladu

19.4.2 Pole rozhraní

Tato klauzule rozšiřuje popis polí ve třídách §15.5 pro pole deklarovaná v rozhraních.

Pole rozhraní jsou deklarována pomocí field_declarations (§15.5.1) s následujícími dalšími pravidly:

  • Jedná se o chybu v době kompilace pro field_declaration deklarování pole instance.

Příklad: Následující program obsahuje statické členy různých druhů:

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}");
    }
}

Výstup je vytvořen.

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

konec příkladu

Informace týkající se přidělení a inicializace statických polí naleznete v §19.4.8 .

19.4.3 Metody rozhraní

Tato klauzule rozšiřuje popis metod ve třídách §15.6 pro metody deklarované v rozhraních.

Metody rozhraní jsou deklarovány pomocí method_declarations (§15.6)). Atributy, return_type, ref_return_type, identifikátor a parameter_list deklarace metody rozhraní mají stejný význam jako atributy deklarace metody ve třídě. Metody rozhraní mají následující další pravidla:

  • method_modifier nezahrnuje override.

  • Metoda, jejíž tělo je středník (;) je abstract; abstract modifikátor není povinný, ale je povolen.

  • Deklarace metody rozhraní, která má tělo bloku nebo tělo výrazu jako method_body je virtual; virtual modifikátor není povinný, ale je povolen.

  • Method_declaration nesmí mít type_parameter_constraints_clause, pokud nemá také type_parameter_list.

  • Seznam požadavků pro platné kombinace modifikátorů uvedených pro metodu třídy je rozšířen takto:

    • Statická deklarace, která není externá, musí mít blokový tělo nebo tělo výrazu jako method_body.
    • Virtuální deklarace, která není extern, musí mít blokový tělo nebo tělo výrazu jako method_body.
    • Soukromá deklarace, která není externá, musí mít blokový tělo nebo tělo výrazu jako method_body.
    • Zapečetěná deklarace, která není extern, musí mít blokový tělo nebo tělo výrazu jako method_body.
    • Asynchronní deklarace musí mít blokový tělo nebo tělo výrazu jako method_body.
  • Všechny typy parametrů metody rozhraní musí být vstupně-bezpečné (§19.2.3.2) a návratový typ musí být buď void nebo výstupní.

  • Všechny typy výstupních nebo referenčních parametrů musí být také bezpečné pro výstup.

    Poznámka: Výstupní parametry musí být kvůli běžným omezením implementace bezpečné pro vstup. koncová poznámka

  • Každé omezení typu třídy, omezení typu rozhraní a omezení parametru typu pro všechny parametry typu metody musí být vstupně-bezpečné.

Tato pravidla zajišťují, aby jakékoli kovariantní nebo kontravariantní použití rozhraní zůstalo beze zbytku typu.

Příklad:

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

je špatně vytvořený, protože použití T jako omezení U parametru typu není bezpečné pro vstup.

Pokud by toto omezení nebylo zavedeno, bylo by možné porušení bezpečnosti typů následujícím způsobem:

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>();

To je vlastně volání C.M<E>. Toto volání však vyžaduje, aby E bylo odvozeno z D, takže bezpečnost typů by zde byla porušena.

konec příkladu

Poznámka: Viz §19.4.2 příklad, který ukazuje nejen statickou metodu s implementací, ale jak je volána Main a má správný návratový typ a podpis, je to také vstupní bod. koncová poznámka

Virtuální metoda s implementací deklarovanou v rozhraní může být přepsána, aby byla abstraktní v odvozené rozhraní. To se označuje jako reabstraction.

Příklad:

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

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

To je užitečné v odvozených rozhraních, kde implementace metody je nevhodná a vhodnější implementace by měla být poskytována implementací tříd. konec příkladu

19.4.4 Vlastnosti rozhraní

Tato klauzule rozšiřuje popis vlastností ve třídách §15.7 pro vlastnosti deklarované v rozhraních.

Vlastnosti rozhraní se deklarují pomocí property_declarations §15.7.1 s následujícími dalšími pravidly:

  • property_modifier nezahrnuje override.

  • Implementace člena explicitního rozhraní nesmí obsahovat accessor_modifier (§15.7.3).

  • Odvozené rozhraní může explicitně implementovat abstraktní vlastnost rozhraní deklarovanou v základním rozhraní.

    Poznámka: Protože rozhraní nemůže obsahovat pole instance, vlastnost rozhraní nemůže být automatická vlastnost instance, protože by to vyžadovalo deklaraci implicitních skrytých polí instance. koncová poznámka

  • Typ vlastnosti rozhraní musí být výstupní-bezpečný, pokud je k dispozici přístupové objekty get, a musí být vstupní-bezpečné, pokud je k dispozici nastavené příslušenství.

  • Deklarace metody rozhraní, která má tělo bloku nebo tělo výrazu jako method_body je virtual; virtual modifikátor není povinný, ale je povolen.

  • Instance property_declaration , která nemá žádnou implementaci, je abstract; abstract modifikátor není povinný, ale je povolen. Nikdy se nepovažuje za automaticky implementovaný majetek (§15.7.4).

19.4.5 Události rozhraní

Tato klauzule rozšiřuje popis událostí ve třídách §15.8 pro události deklarované v rozhraních.

Události rozhraní se deklarují pomocí event_declarations (§15.8.1) s následujícími dalšími pravidly:

  • event_modifier nezahrnuje override.
  • Odvozené rozhraní může implementovat událost abstraktního rozhraní deklarovanou v základním rozhraní (§15.8.5).
  • Jedná se o chybu v době kompilace pro variable_declarators v instanci , event_declaration obsahovat všechny variable_initializers.
  • Událost instance s virtual modifikátory musí sealed deklarovat přístupové objekty. Nikdy se nepovažuje za automaticky implementovanou akci podobné poli (§15.8.2).
  • Událost instance s modifikátorem abstract nesmí deklarovat přístupové objekty.
  • Typ události rozhraní musí být bezpečný pro vstup.

19.4.6 Indexery rozhraní

Tato klauzule rozšiřuje popis indexerů ve třídách §15.9 pro indexery deklarované v rozhraních.

Indexery rozhraní jsou deklarovány pomocí indexer_declarations (§15.9) s následujícími dalšími pravidly:

  • indexer_modifier nezahrnuje override.

  • Indexer_declaration, která obsahuje tělo výrazu nebo obsahuje příslušenství s tělem bloku nebo tělem výrazu, je virtual; virtual modifikátor není povinný, ale je povolený.

  • Indexer_declaration, jejichž přistupná těla jsou středníky (;) je abstract; abstract modifikátor není povinný, ale je povolen.

  • Všechny typy parametrů indexeru rozhraní musí být vstupní-bezpečné (§19.2.3.2).

  • Všechny typy výstupních nebo referenčních parametrů musí být také bezpečné pro výstup.

    Poznámka: Výstupní parametry musí být kvůli běžným omezením implementace bezpečné pro vstup. koncová poznámka

  • Typindexho zařízení musí být výstupově bezpečný, pokud je k dispozici přístupový objekt get, a musí být bezpečný pro vstup, pokud je k dispozici nastavený přístupový objekt.

19.4.7 Operátory rozhraní

Tato klauzule rozšiřuje popis operator_declaration členů ve třídách §15.10 pro operátory deklarované v rozhraních.

Operator_declaration v rozhraní je implementace (§19.1).

Jedná se o chybu v době kompilace, která rozhraní deklaruje operátor převodu, rovnosti nebo nerovnosti.

19.4.8 Rozhraní statických konstruktorů

Tato klauzule rozšiřuje popis statických konstruktorů ve třídách §15.12 pro statické konstruktory deklarované v rozhraních.

Statický konstruktor pro uzavřené rozhraní (§8.4.3) se v dané doméně aplikace provede maximálně jednou. Spuštění statického konstruktoru se aktivuje první z následujících akcí v rámci domény aplikace:

  • Na kterýkoli ze statických členů rozhraní se odkazuje.
  • Main Před voláním metody je volána rozhraní obsahující metodu Main (§7.1), ve které začíná provádění.
  • Toto rozhraní poskytuje implementaci člena a k této implementaci se přistupuje jako k nejvýraznějšímu provedení (§19.4.10) daného člena.

Poznámka: V případě, že se neprovedou žádné z předchozích akcí, nemusí statický konstruktor rozhraní spustit pro program, kde jsou vytvořeny a použity instance typů, které implementují rozhraní. koncová poznámka

Chcete-li inicializovat nový uzavřený typ rozhraní, nejprve se vytvoří nová sada statických polí pro daný uzavřený typ. Každé ze statických polí se inicializuje na výchozí hodnotu. Dále se pro tato statická pole spustí inicializátory statických polí. Nakonec se spustí statický konstruktor.

Poznámka: Viz §19.4.2 příklad použití různých druhů statických členů (včetně metody Main) deklarovaných v rámci rozhraní. koncová poznámka

19.4.9 Rozhraní vnořených typů

Tato klauzule rozšiřuje popis vnořených typů ve třídách §15.3.9 pro vnořené typy deklarované v rozhraních.

Jedná se o chybu deklarování typu třídy, typu struktury nebo výčtu v rozsahu parametru typu deklarovaného pomocí variance_annotation (§19.2.3.1).

Příklad: Deklarace C níže je chyba.

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

konec příkladu

19.4.10 nejvýraznější implementace

Každá třída a struktura musí mít nejvýkonnější implementaci pro každý virtuální člen deklarovaný ve všech rozhraních implementovaných tímto typem mezi implementacemi, které se zobrazují v typu nebo jeho přímých a nepřímých rozhraních. Nejvýraznější implementace je jedinečná implementace, která je konkrétnější než každá jiná implementace.

Poznámka: Nejvýkonnější pravidlo implementace zajišťuje, že nejednoznačnost vyplývající z dědičnosti rozhraní kosočtvercového rozhraní je vyřešena explicitně programátorem v okamžiku, kdy dojde ke konfliktu. koncová poznámka

Pro typ T , který je strukturou nebo třídou, která implementuje rozhraní I2 , a I3, kde I2 a I3 oba odvozují přímo nebo nepřímo z rozhraní I , který deklaruje člen M, nejvýkonnější implementace M je:

  • Pokud T deklaruje implementaci I.M, je tato implementace nejvýraznější implementací.
  • Jinak, pokud T je třída a přímá nebo nepřímá základní třída deklaruje implementaci I.M, nejvíce odvozené základní třídy T je nejvýraznější implementace.
  • V opačném případě, pokud I2 a jsou rozhraní implementovaná I3 a T odvozena z I3 buď přímo nebo nepřímo, I2 je konkrétnější implementace než I3.MI2.M .
  • Jinak ani I2.MI3.M nejsou konkrétnější a dojde k chybě.

Příklad:

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();
}

Nejkonfigurovanější pravidlo implementace zajišťuje, aby konflikt (tj. nejednoznačnost vyplývající z dědičnosti diamantů) byl vyřešen explicitně programátorem v okamžiku, kdy k konfliktu dochází. konec příkladu

19.4.11 Přístup člena rozhraní

K členům rozhraní se přistupuje prostřednictvím přístupů členů (§12.8.7) a indexerového přístupu (§12.8.12.4) formuláře I.M a I[A], kde I je typ rozhraní, je konstantní, M pole, metoda, vlastnost nebo událost tohoto typu rozhraní a A je seznam argumentů indexeru.

Ve třídě Ds přímou nebo nepřímou základní třídou B, kde B přímo nebo nepřímo implementuje rozhraní I a I definuje metodu M(), je výraz base.M() platný pouze v případě, že base.M() staticky (§12.3) vytvoří vazbu na implementaci M() typu třídy.

Pro rozhraní, která jsou výhradně jednoduchá dědičnost (každé rozhraní v řetězci dědičnosti má přesně nula nebo jedno přímé základní rozhraní), jsou účinky vyhledávání členů (§12.5), vyvolání metody (§12.8.10.2) a přístup indexeru (§12.8.12.4) přesně stejné jako u tříd a struktur: Více odvozených členů skryje méně odvozených členů se stejným názvem nebo podpisem. U rozhraní s více dědičnostmi ale může dojít k nejednoznačnostem, když dva nebo více nesouvisejících základních rozhraní deklarují členy se stejným názvem nebo podpisem. Tato dílčí ukázka ukazuje několik příkladů, z nichž některé vedou k nejednoznačnostem a jiným, které ne. Ve všech případech lze explicitní přetypování použít k vyřešení nejednoznačností.

Příklad: V následujícím kódu

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

První příkaz způsobí chybu při kompilaci, protože při vyhledávání členu (§12.5) je Count v IListCounter nejednoznačné. Jak je znázorněno v příkladu, nejednoznačnost se přeloží přetypováním x na odpovídající základní typ rozhraní. Takové přetypování nemají žádné náklady za běhu – pouze se skládají z zobrazení instance jako méně odvozeného typu v době kompilace.

konec příkladu

Příklad: V následujícím kódu

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

vyvolání n.Add(1) vybere IInteger.Add použitím pravidel řešení přetížení §12.6.4. Podobně vyvolání n.Add(1.0) vybere IDouble.Add. Při vložení explicitních přetypování existuje pouze jedna metoda kandidáta, a proto není nejednoznačnost.

konec příkladu

Příklad: V následujícím kódu

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

člen IBase.F je skrytý ILeft.F členem. Vyvolání d.F(1) tedy vybere ILeft.F, i když IBase.F se zdá, že není skryta v přístupové cestě, která vede .IRight

Intuitivní pravidlo pro skrytí v rozhraních s více dědičností je jednoduché: Pokud je člen skrytý v jakékoli přístupové cestě, je skrytý ve všech přístupových cestách. Vzhledem k tomu, že přístupová cesta od IDerived k ILeftIBase skrytí IBase.F, člen je také skrytý v přístupové cestě od IDerived do IRightIBase.

konec příkladu

19.5 Kvalifikované názvy členů rozhraní

Člen rozhraní se někdy označuje jeho kvalifikovaným názvem člena rozhraní. Kvalifikovaný název člena rozhraní se skládá z názvu rozhraní, ve kterém je člen deklarován, následovaný tečkou následovanou názvem člena. Kvalifikovaný název člena odkazuje na rozhraní, ve kterém je člen deklarován.

Příklad: Vzhledem k deklaracím

interface IControl
{
    void Paint();
}

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

kvalifikovaný název Paint je IControl.Paint a kvalifikovaný název SetText je ITextBox.SetText. V předchozím příkladu není možné odkazovat na Paint .ITextBox.Paint

konec příkladu

Pokud je rozhraní součástí oboru názvů, může kvalifikovaný název člena rozhraní obsahovat název oboru názvů.

Příklad:

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

GraphicsLib V rámci oboru názvů jsou IPolygon.CalculateAreaGraphicsLib.IPolygon.CalculateArea kvalifikované názvy členů rozhraní pro metoduCalculateArea.

konec příkladu

19.6 Implementace rozhraní

19.6.1 Obecné

Rozhraní mohou být implementována třídami a strukturami. Chcete-li označit, že třída nebo struktura přímo implementuje rozhraní, je rozhraní zahrnuto v seznamu základních tříd třídy nebo struktury.

Třída nebo struktura C , která implementuje rozhraní I , musí poskytovat nebo dědit implementaci pro každého člena deklarovaného v I tom, který C má přístup. Veřejné členy I mohou být definovány ve veřejných členech C. Neveřejné členy deklarované v I tom, že jsou přístupné C , mohou být definovány při C použití explicitní implementace rozhraní (§19.6.2).

Člen odvozeného typu, který splňuje mapování rozhraní (§19.6.5), ale neimplementuje odpovídající člen základního rozhraní zavádí nový člen. K tomu dochází v případě, že je k definování člena rozhraní vyžadována explicitní implementace rozhraní.

Příklad:

interface ICloneable
{
    object Clone();
}

interface IComparable
{
    int CompareTo(object other);
}

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

konec příkladu

Třída nebo struktura, která přímo implementuje rozhraní také implicitně implementuje všechna základní rozhraní rozhraní. To platí i v případě, že třída nebo struktura explicitně nevypisuje všechna základní rozhraní v seznamu základních tříd.

Příklad:

interface IControl
{
    void Paint();
}

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

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

Zde třída TextBox implementuje jak IControl a ITextBox.

konec příkladu

Když třída C přímo implementuje rozhraní, všechny třídy odvozené z C také implementují rozhraní implicitně.

Základní rozhraní zadaná v deklaraci třídy mohou být vytvořena typy rozhraní (§8.4, §19.2).

Příklad: Následující kód ukazuje, jak může třída implementovat vytvořené typy rozhraní:

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

konec příkladu

Základní rozhraní obecné deklarace třídy splňují pravidlo jedinečnosti popsané v §19.6.3.

19.6.2 Explicitní implementace členů rozhraní

Pro účely implementace rozhraní může třída, struktura nebo rozhraní deklarovat explicitní implementace člena rozhraní. Explicitní implementace člena rozhraní je metoda, vlastnost, událost nebo indexer deklarace, která odkazuje na kvalifikovaný název člena rozhraní. Třída nebo struktura, která implementuje neveřejný člen v základním rozhraní musí deklarovat explicitní implementaci člena rozhraní. Rozhraní, které implementuje člena v základním rozhraní, musí deklarovat explicitní implementaci člena rozhraní.

Odvozený člen rozhraní, který splňuje mapování rozhraní (§19.6.5), skryje člen základního rozhraní (§7.7.2). Kompilátor vydá upozornění, pokud new není k dispozici modifikátor.

Příklad:

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) {...}
}

Tady IDictionary<int,T>.this jsou IDictionary<int,T>.Add explicitní implementace členů rozhraní.

konec příkladu

Příklad: V některých případech nemusí být název člena rozhraní vhodný pro implementační třídu, v takovém případě může být člen rozhraní implementován pomocí explicitní implementace člena rozhraní. Třída implementující abstrakci souboru, například by pravděpodobně implementovala členovou Close funkci, která má vliv na uvolnění prostředku souboru, a implementovala Dispose metodu IDisposable rozhraní pomocí explicitní implementace člena rozhraní:

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

konec příkladu

Explicitní implementaci člena rozhraní není možné získat přístup prostřednictvím jeho kvalifikovaného názvu člena rozhraní při vyvolání metody, přístupu k vlastnostem, přístupu k událostem nebo přístupu indexeru. K explicitní implementaci člena instance rozhraní lze přistupovat pouze prostřednictvím instance rozhraní. V takovém případě se na to odkazuje jednoduše jeho názvem člena. Explicitní implementace statického členu rozhraní je přístupná pouze prostřednictvím názvu rozhraní.

Jedná se o chybu v době kompilace pro explicitní implementaci člena rozhraní, aby zahrnovaly jakékoli modifikátory (§15.6) jiné než extern nebo async.

Implementace metody explicitního rozhraní dědí všechna omezení parametrů typu z rozhraní.

Type_parameter_constraints_clause při implementaci metody explicitního rozhraní se může skládat pouze z classstructprimary_constraint použitých na type_parameter, které jsou podle zděděných omezení známy jako referenční nebo hodnotové typy. Jakýkoli typ formuláře T? v podpisu implementace explicitní metody rozhraní, kde T je parametr typu, je interpretován takto:

  • class Pokud je pro parametr T typu přidáno omezení, T? je odkazový typ s možnou hodnotou null; jinak
  • Pokud není přidané žádné omezení nebo je přidáno omezení struct, pak pro typový parametr T je T? nullable typ hodnoty.

Příklad: Následující příklad ukazuje, jak fungují pravidla při zapojení parametrů typu:

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

Bez omezení parametru typu where T : class nelze přepsat metodu základu, která má parametr typu s referenčním typem. konec příkladu

Poznámka: Explicitní implementace členů rozhraní mají jiné vlastnosti přístupnosti než ostatní členy. Vzhledem k tomu, že explicitní implementace členů rozhraní nejsou nikdy přístupné prostřednictvím kvalifikovaného názvu člena rozhraní při vyvolání metody nebo přístupu k vlastnosti, jsou ve smyslu privátní. Vzhledem k tomu, že k nim lze přistupovat prostřednictvím rozhraní, jsou ve smyslu také jako veřejné jako rozhraní, ve kterém jsou deklarovány. Explicitní implementace členů rozhraní slouží ke dvěma primárním účelům:

  • Vzhledem k tomu, že implementace členů explicitního rozhraní nejsou přístupné prostřednictvím instancí třídy nebo struktury, umožňují, aby implementace rozhraní byly vyloučeny z veřejného rozhraní třídy nebo struktury. To je zvlášť užitečné, když třída nebo struktura implementuje interní rozhraní, které není zajímavé pro příjemce této třídy nebo struktury.
  • Explicitní implementace členů rozhraní umožňují nejednoznačnost členů rozhraní se stejným podpisem. Bez explicitních implementací členů rozhraní by nebylo možné, aby třída, struktura nebo rozhraní měly různé implementace členů rozhraní se stejným podpisem a návratovým typem, stejně jako by nebylo možné, aby třída, struktura nebo rozhraní měla jakoukoli implementaci u všech členů rozhraní se stejným podpisem, ale s různými návratovými typy.

koncová poznámka

Aby byla implementace explicitního člena rozhraní platná, třída, struktura nebo rozhraní musí pojmenovat rozhraní v seznamu základních tříd nebo základního rozhraní, který obsahuje člena, jehož kvalifikovaný název člena rozhraní, typ, počet parametrů typu a typy parametrů přesně odpovídají typům implementace explicitního člena rozhraní. Pokud má člen funkce rozhraní pole parametrů, je povolen odpovídající parametr přidružené explicitní implementace člena rozhraní, ale není vyžadován, aby params měl modifikátor. Pokud člen funkce rozhraní nemá pole parametrů, přidružená implementace explicitního členu rozhraní nebude mít pole parametrů.

Příklad: Proto v následující třídě

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

deklarace IComparable.CompareTo výsledků při chybě v době kompilace, protože IComparable není uvedena v seznamu Shape základních tříd a není základním rozhraním ICloneable. Stejně tak v deklaracích

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

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

deklarace ICloneable.Clone v Ellipse důsledku toho způsobí chybu v době kompilace, protože ICloneable není explicitně uvedena v seznamu Ellipsezákladních tříd .

konec příkladu

Kvalifikovaný název člena rozhraní implementace explicitního člena rozhraní odkazuje na rozhraní, ve kterém byl člen deklarován.

Příklad: Proto v deklaracích

interface IControl
{
    void Paint();
}

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

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

Explicitní implementace člena rozhraní Malování musí být zapsána jako IControl.Paint, nikoli ITextBox.Paint.

konec příkladu

19.6.3 Jedinečnost implementovaných rozhraní

Rozhraní implementovaná obecnou deklarací typu zůstanou jedinečná pro všechny možné konstruované typy. Bez tohoto pravidla by nebylo možné určit správnou metodu volání určitých konstruovaných typů.

Příklad: Předpokládejme, že byla povolena deklarace obecné třídy takto:

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() {...}
}

Bylo by to povoleno, nebylo by možné určit, který kód se má provést v následujícím případě:

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

konec příkladu

Chcete-li zjistit, zda je seznam rozhraní deklarace obecného typu platný, jsou provedeny následující kroky:

  • Pojďme L být seznam rozhraní přímo určených v obecné třídě, struktuře nebo deklaraci Crozhraní .
  • Přidat do L všech základních rozhraní rozhraní již v L.
  • Odeberte všechny duplicity z L.
  • Pokud by byl vytvořen jakýkoli možný vytvořený typ C , po nahrazení argumentů typu do L, způsobit, že dvě rozhraní budou L identické, pak deklarace C je neplatná. Při určování všech možných konstruovaných typů se deklarace omezení nepovažují.

Poznámka: V deklaraci X třídy výše se seznam L rozhraní skládá a l<U>I<V>. Deklarace je neplatná, protože jakýkoli vytvořený typ se stejným typem UV by způsobil, že tato dvě rozhraní budou identické typy. koncová poznámka

Rozhraní zadaná na různých úrovních dědičnosti je možné sjednotit:

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() {...}
}

Tento kód je platný, i když Derived<U,V> implementuje obojí I<U> i I<V>. Kód

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

vyvolá metodu, Derivedneboť Derived<int,int>' účinně znovu implementuje I<int> (§19.6.7).

19.6.4 Implementace obecných metod

Pokud obecná metoda implicitně implementuje metodu rozhraní, omezení pro každý parametr typu metody musí být v obou deklaracích ekvivalentní (po nahrazení parametrů typu rozhraní příslušnými argumenty typu), kde jsou parametry typu metody identifikovány pořadovými pozicemi, zleva doprava.

Příklad: V následujícím kódu:

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
}

metoda C.F<T> implicitně implementuje I<object,C,string>.F<T>. V tomto případě není nutné (ani povoleno) určit omezeníC.F<T>, T: object protože object je implicitní omezení pro všechny parametry typu. Metoda C.G<T> implicitně implementuje I<object,C,string>.G<T> , protože omezení odpovídají omezením v rozhraní, po nahrazení parametrů typu rozhraní odpovídajícími argumenty typu. Omezení metody C.H<T> je chyba, protože zapečetěné typy (string v tomto případě) nelze použít jako omezení. Vynechání omezení by také bylo chybou, protože omezení implementace implicitních metod rozhraní se vyžadují ke shodě. Proto není možné implicitně implementovat I<object,C,string>.H<T>. Tuto metodu rozhraní lze implementovat pouze pomocí explicitní implementace člena rozhraní:

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

V tomto případě explicitní implementace člena rozhraní vyvolá veřejnou metodu s přísně slabšími omezeními. Přiřazení z t na s je platné, protože T dědí omezení T: string, i když toto omezení není ve zdrojovém kódu vyjádřitelné. konec příkladu

Poznámka: Pokud obecná metoda explicitně implementuje metodu rozhraní bez omezení prováděcí metody (§15.7.1, §19.6.2). koncová poznámka

19.6.5 Mapování rozhraní

Třída nebo struktura poskytuje implementace všech abstraktních členů rozhraní uvedených v seznamu základních tříd třídy nebo struktury. Proces vyhledání implementací členů rozhraní v implementační třídě nebo struktuře se označuje jako mapování rozhraní.

Mapování rozhraní pro třídu nebo strukturu C vyhledá implementaci pro každý člen každého rozhraní zadaného v seznamu Czákladních tříd . Implementace určitého členu I.Mrozhraní , kde I je rozhraní, ve kterém je člen M deklarován, je určeno prozkoumáním každé třídy, rozhraní nebo struktury S, počínaje C a opakováním pro každou následnou základní třídu a implementované rozhraní C, dokud se neshoda nenachází:

  • Obsahuje-li S deklaraci explicitní implementace členu rozhraní, která odpovídá I , a Mpak tento člen je implementace I.M.
  • V opačném případě, pokud S obsahuje deklaraci nestatického veřejného členu, který odpovídá M, pak tento člen je implementace I.M. Pokud se shoduje více než jeden člen, není určeno, který člen je implementací I.M. K této situaci může dojít pouze v případě S , že je vytvořený typ, kde dva členy deklarované v obecném typu mají různé podpisy, ale argumenty typu činí jejich podpisy identické.

K chybě v době kompilace dochází v případě, že implementace nelze nalézt pro všechny členy všech rozhraní zadaných v seznamu Czákladních tříd . Členy rozhraní zahrnují ty členy, které jsou zděděny ze základních rozhraní.

Členové konstruovaného typu rozhraní se považují za všechny parametry typu nahrazené odpovídajícími argumenty typu, jak je uvedeno v §15.3.3.

Příklad: Například vzhledem k deklaraci obecného rozhraní:

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

vytvořené rozhraní I<string[]> má členy:

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

konec příkladu

Pro účely mapování rozhraní odpovídá třída, rozhraní nebo člen A struktury člen rozhraní B v těchto případech:

  • A a B jsou metody a název, typ a seznamy A parametrů a B jsou identické.
  • A a B jsou vlastnosti, název a typ A a B jsou identické a A mají stejné přístupové objekty jako B (A je povoleno mít další přístupové objekty, pokud se nejedná o explicitní implementaci člena rozhraní).
  • A a B jsou události a název a typ A a B jsou identické.
  • A a B jsou indexery, seznamy A typů a parametrů a B jsou identické a A mají stejné přístupové objekty jako B (A je povoleno mít další přístupové objekty, pokud to není explicitní implementace člena rozhraní).

Významné důsledky algoritmu mapování rozhraní jsou:

  • Explicitní implementace členů rozhraní mají přednost před ostatními členy ve stejné třídě nebo struktuře při určování třídy nebo člena struktury, který implementuje člen rozhraní.
  • Neveřejné ani statické členy se neúčastní mapování rozhraní.

Příklad: V následujícím kódu

interface ICloneable
{
    object Clone();
}

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

Element ICloneable.Clone ve struktuře C se stane implementací Clone v ICloneable, protože implementace explicitních členů rozhraní mají přednost před ostatními členy.

konec příkladu

Pokud třída nebo struktura implementuje dvě nebo více rozhraní obsahující člen se stejným názvem, typem a typy parametrů, je možné mapovat každý z těchto členů rozhraní na jednu třídu nebo člen struktury.

Příklad:

interface IControl
{
    void Paint();
}

interface IForm
{
    void Paint();
}

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

Zde jsou Paint metody obou IControl a IForm jsou mapovány na metodu Paint v Page. Je samozřejmě také možné mít samostatné explicitní implementace členů rozhraní pro tyto dvě metody.

konec příkladu

Pokud třída nebo struktura implementuje rozhraní, které obsahuje skryté členy, může být nutné některé členy implementovat prostřednictvím explicitních implementací členů rozhraní.

Příklad:

interface IBase
{
    int P { get; }
}

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

Implementace tohoto rozhraní by vyžadovala aspoň jednu explicitní implementaci člena rozhraní a vyžadovala by jednu z následujících forem.

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() {...}
}

konec příkladu

Když třída implementuje více rozhraní, která mají stejné základní rozhraní, může existovat pouze jedna implementace základního rozhraní.

Příklad: V následujícím kódu

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) {...}
}

není možné mít samostatné implementace pro pojmenované IControl v seznamu základních tříd, IControl zděděné a ITextBoxIControl zděděné .IListBox Vskutku neexistuje pojem samostatné identity pro tato rozhraní. Spíše implementace ITextBoxa IListBox sdílejí stejnou implementaci IControl, a ComboBox je jednoduše považována za implementaci tří rozhraní, IControl, ITextBoxa IListBox.

konec příkladu

Členové základní třídy se účastní mapování rozhraní.

Příklad: V následujícím kódu

interface Interface1
{
    void F();
}

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

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

metoda FClass1 je použita při Class2's provádění Interface1.

konec příkladu

19.6.6 Dědičnost implementace rozhraní

Třída dědí všechny implementace rozhraní poskytované základními třídami.

Bez explicitní opětovné implementace rozhraní nemůže odvozená třída žádným způsobem změnit mapování rozhraní, které dědí ze svých základních tříd.

Příklad: V deklaracích

interface IControl
{
    void Paint();
}

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

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

metoda Paint skryje TextBox metodu Paint v Control, ale nemění mapování Control.Paint na IControl.Paint, a volání Paint prostřednictvím instancí třídy a rozhraní instance budou mít následující účinky

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();

konec příkladu

Pokud je však metoda rozhraní mapována na virtuální metodu ve třídě, je možné odvozené třídy přepsat virtuální metodu a změnit implementaci rozhraní.

Příklad: Přepsání výše uvedených deklarací na

interface IControl
{
    void Paint();
}

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

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

nyní budou pozorovány následující účinky.

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();

konec příkladu

Vzhledem k tomu, že explicitní implementace členů rozhraní nelze deklarovat virtuální, není možné přepsat explicitní implementaci člena rozhraní. Je však naprosto platné pro explicitní implementaci člena rozhraní volat jinou metodu a že jiná metoda může být deklarována virtuální, aby bylo možné odvozené třídy přepsat.

Příklad:

interface IControl
{
    void Paint();
}

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

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

Třídy odvozené z Control mohou specializovat implementaci IControl.Paint přepsáním PaintControl metody.

konec příkladu

19.6.7 Opětovné implementace rozhraní

Třída, která dědí implementaci rozhraní, je povoleno znovu implementovat rozhraní zahrnutím do seznamu základních tříd.

Opětovná implementace rozhraní se řídí přesně stejnými pravidly mapování rozhraní jako počáteční implementace rozhraní. Zděděné mapování rozhraní tedy nemá žádný vliv na mapování rozhraní vytvořené pro opětovné provedení rozhraní.

Příklad: V deklaracích

interface IControl
{
    void Paint();
}

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

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

skutečnost, že Control mapování IControl.Paint na nemá vliv na Control.IControl.Paint re-implementaci v MyControl, která se mapuje IControl.Paint na MyControl.Paint.

konec příkladu

Zděděné deklarace veřejného členu a zděděné deklarace členů explicitního rozhraní se účastní procesu mapování rozhraní pro znovu implementovaná rozhraní.

Příklad:

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() {}
}

Zde implementace IMethods v Derived mapách metod rozhraní na Derived.F, Base.IMethods.G, Derived.IMethods.Ha Base.I.

konec příkladu

Když třída implementuje rozhraní, implicitně také implementuje všechna základní rozhraní rozhraní. Podobně je opětovná implementace rozhraní také implicitně re-implementace všech základních rozhraní rozhraní.

Příklad:

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() {...}
}

Zde se opětovná implementace IDerived také znovu implementuje IBase, mapování IBase.F na D.F.

konec příkladu

19.6.8 Abstraktní třídy a rozhraní

Stejně jako abstraktní třída poskytuje abstraktní třída implementaci všech abstraktních členů rozhraní, která jsou uvedena v seznamu základních tříd třídy. Abstraktní třída však může mapovat metody rozhraní na abstraktní metody.

Příklad:

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

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

Zde provádění IMethods map F a G na abstraktní metody, které se přepíše v ne abstraktních třídách, které jsou odvozeny .C

konec příkladu

Explicitní implementace členů rozhraní nemohou být abstraktní, ale explicitní implementace členů rozhraní jsou samozřejmě povoleno volat abstraktní metody.

Příklad:

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();
}

V této části by se k přepsání C vyžadovaly ne abstraktní třídy, které jsou odvozenyFF, a GGtím by poskytovaly skutečnou implementaci IMethods.

konec příkladu