Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
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; } }
Xje kovariantní,Yje kontravariantní aZje 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í:
-
Tje parametr kontravariantního typu. -
Tje typ pole s nebezpečným typem výstupního prvku. -
Tje typSᵢ,... Aₑrozhraní nebo delegáta vytvořený z obecného typuS<Xᵢ, ... Xₑ>, kde platí alespoň pro jednuAᵢz následujících možností:-
Xᵢje kovariantní nebo invariantní aAᵢje nebezpečný pro výstup. -
Xᵢje kontravariantní nebo invariantní aAᵢje nebezpečný.
-
Typ T je nebezpečný , pokud platí jedna z následujících možností:
-
Tje kovariantní parametr typu. -
Tje typ pole se vstupním nebezpečným typem elementu. -
Tje typS<Aᵢ,... Aₑ>rozhraní nebo delegáta vytvořený z obecného typuS<Xᵢ, ... Xₑ>, kde platí alespoň pro jednuAᵢz následujících možností:-
Xᵢje kovariantní nebo invariantní aAᵢje nebezpečný pro vstup. -
Xᵢje kontravariantní nebo invariantní aAᵢ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 zAᵢdoBᵢ -
Xᵢje kontravariantní a implicitní odkaz nebo převod identity existuje zBᵢdoAᵢ -
Xᵢje invariantní a převod identity existuje zAᵢdoBᵢ
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
privaterozhraníinternalv interface_basepublicrozhraní. 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í
IComboBoxjsouIControl,ITextBoxaIListBox. Jinými slovy,IComboBoxvýše uvedené rozhraní dědí členySetTextaSetItemstaké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í
IDeriveddědí metoduCombinepo nahrazení parametruTstring[,]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).
-
abstractModifiká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, pokudsealednení použit neboprivatemodifikátor.virtualModifikátor může být explicitně uveden. - Člen
privaterozhraní nebosealedfunkce musí mít subjekt. - Člen
privatefunkce nesmí mít modifikátorsealed. - 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
objectnejsou, přísně řečeno, členy jakéhokoli rozhraní (§19.4). Členy třídyobjectjsou 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í
IAs implementací členaMa vlastnostiP. Implementační typCneposkytuje implementaci aniMPpro . Musí být přístupné prostřednictvím odkazu, jehož typ kompilace-čas je rozhraní, které je implicitně konvertibilní naIAneboIB. Tyto členy nejsou nalezeny prostřednictvím vyhledávání členů v proměnné typuC.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
IAaIBčlenMje přístupný přímo podle názvu. V rámci metodyMainvšak nemůžeme napsatc.M()neboc.P, protože tyto názvy nejsou viditelné. K jejich nalezení je potřeba přetypovat na příslušný typ rozhraní. DeklaraceMinIBpoužívá explicitní syntaxi implementace rozhraní. To je nezbytné k tomu, aby tato metoda přepsala hodnotu vIA; modifikátoroverridenelze 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 = 50konec 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 (
;) jeabstract;abstractmodifikátor není povinný, ale je povolen.Deklarace metody rozhraní, která má tělo bloku nebo tělo výrazu jako method_body je
virtual;virtualmodifiká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ď
voidnebo 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í
Tjako omezeníUparametru 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, abyEbylo odvozeno zD, 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
Maina 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;virtualmodifikátor není povinný, ale je povolen.Instance property_declaration , která nemá žádnou implementaci, je
abstract;abstractmodifiká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
virtualmodifikátory musísealeddeklarovat přístupové objekty. Nikdy se nepovažuje za automaticky implementovanou akci podobné poli (§15.8.2). - Událost instance s modifikátorem
abstractnesmí 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;virtualmodifikátor není povinný, ale je povolený.Indexer_declaration, jejichž přistupná těla jsou středníky (
;) jeabstract;abstractmodifiká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.
-
MainPřed voláním metody je volána rozhraní obsahující metoduMain(§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
Cníž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
Tdeklaruje implementaciI.M, je tato implementace nejvýraznější implementací. - Jinak, pokud
Tje třída a přímá nebo nepřímá základní třída deklaruje implementaciI.M, nejvíce odvozené základní třídyTje nejvýraznější implementace. - V opačném případě, pokud
I2a jsou rozhraní implementovanáI3aTodvozena zI3buď přímo nebo nepřímo,I2je konkrétnější implementace nežI3.MI2.M. - Jinak ani
I2.MI3.Mnejsou 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
CountvIListCounternejednoznačné. Jak je znázorněno v příkladu, nejednoznačnost se přeloží přetypovánímxna 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)vybereIInteger.Addpoužitím pravidel řešení přetížení §12.6.4. Podobně vyvolánín.Add(1.0)vybereIDouble.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.Fje skrytýILeft.Fčlenem. Vyvoláníd.F(1)tedy vybereILeft.F, i kdyžIBase.Fse zdá, že není skryta v přístupové cestě, která vede .IRightIntuitivní 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
IDerivedkILeftIBaseskrytíIBase.F, člen je také skrytý v přístupové cestě odIDeriveddoIRightIBase.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
PaintjeIControl.Painta kvalifikovaný název SetText jeITextBox.SetText. V předchozím příkladu není možné odkazovat naPaint.ITextBox.Paintkonec 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(); } }
GraphicsLibV rámci oboru názvů jsouIPolygon.CalculateAreaGraphicsLib.IPolygon.CalculateAreakvalifikované 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
TextBoximplementuje jakIControlaITextBox.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>.thisjsouIDictionary<int,T>.Addexplicitní 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
Closefunkci, která má vliv na uvolnění prostředku souboru, a implementovalaDisposemetoduIDisposablerozhraní 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:
-
classPokud je pro parametrTtypu 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ý parametrTjeT?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 : classnelze 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.CompareTovýsledků při chybě v době kompilace, protožeIComparablenení uvedena v seznamuShapezákladních tříd a není základním rozhranímICloneable. Stejně tak v deklaracíchclass Shape : ICloneable { object ICloneable.Clone() {...} } class Ellipse : Shape { object ICloneable.Clone() {...} // invalid }deklarace
ICloneable.ClonevEllipsedůsledku toho způsobí chybu v době kompilace, protožeICloneablenení explicitně uvedena v seznamuEllipsezá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, nikoliITextBox.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
Lbýt seznam rozhraní přímo určených v obecné třídě, struktuře nebo deklaraciCrozhraní . - Přidat do
Lvšech základních rozhraní rozhraní již vL. - Odeberte všechny duplicity z
L. - Pokud by byl vytvořen jakýkoli možný vytvořený typ
C, po nahrazení argumentů typu doL, způsobit, že dvě rozhraní budouLidentické, pak deklaraceCje neplatná. Při určování všech možných konstruovaných typů se deklarace omezení nepovažují.
Poznámka: V deklaraci
Xtřídy výše se seznamLrozhraní skládá al<U>I<V>. Deklarace je neplatná, protože jakýkoli vytvořený typ se stejným typemUVby 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ě implementujeI<object,C,string>.F<T>. V tomto případě není nutné (ani povoleno) určit omezeníC.F<T>,T: objectprotožeobjectje implicitní omezení pro všechny parametry typu. MetodaC.G<T>implicitně implementujeI<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í metodyC.H<T>je chyba, protože zapečetěné typy (stringv 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ě implementovatI<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
Tdě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
Sdeklaraci explicitní implementace členu rozhraní, která odpovídáI, aMpak tento člen je implementaceI.M. - V opačném případě, pokud
Sobsahuje deklaraci nestatického veřejného členu, který odpovídáM, pak tento člen je implementaceI.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:
-
AaBjsou metody a název, typ a seznamyAparametrů aBjsou identické. -
AaBjsou vlastnosti, název a typAaBjsou identické aAmají stejné přístupové objekty jakoB(Aje povoleno mít další přístupové objekty, pokud se nejedná o explicitní implementaci člena rozhraní). -
AaBjsou události a název a typAaBjsou identické. -
AaBjsou indexery, seznamyAtypů a parametrů aBjsou identické aAmají stejné přístupové objekty jakoB(Aje 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.Cloneve struktuřeCse stane implementacíClonevICloneable, 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
Paintmetody obouIControlaIFormjsou mapovány na metoduPaintvPage. 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é
IControlv seznamu základních tříd,IControlzděděné aITextBoxIControlzděděné .IListBoxVskutku neexistuje pojem samostatné identity pro tato rozhraní. Spíše implementaceITextBoxaIListBoxsdílejí stejnou implementaciIControl, aComboBoxje jednoduše považována za implementaci tří rozhraní,IControl,ITextBoxaIListBox.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
FClass1je použita přiClass2'sprová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
PaintskryjeTextBoxmetoduPaintvControl, ale nemění mapováníControl.PaintnaIControl.Paint, a voláníPaintprostřednictvím instancí třídy a rozhraní instance budou mít následující účinkyControl 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
Controlmohou specializovat implementaciIControl.PaintpřepsánímPaintControlmetody.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
ControlmapováníIControl.Paintna nemá vliv naControl.IControl.Paintre-implementaci vMyControl, která se mapujeIControl.PaintnaMyControl.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
IMethodsvDerivedmapách metod rozhraní naDerived.F,Base.IMethods.G,Derived.IMethods.HaBase.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
IDerivedtaké znovu implementujeIBase, mapováníIBase.FnaD.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í
IMethodsmapFaGna abstraktní metody, které se přepíše v ne abstraktních třídách, které jsou odvozeny .Ckonec 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í
Cvyžadovaly ne abstraktní třídy, které jsou odvozenyFF, aGGtím by poskytovaly skutečnou implementaciIMethods.konec příkladu
ECMA C# draft specification