Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
18.1 Általános
Az interfész egy szerződést határoz meg. Az interfészt megvalósító osztálynak vagy szerkezetnek be kell tartania a szerződését. Egy interfész több alapillesztőtől örökölhet, az osztály vagy a szerkezet pedig több interfészt is implementálhat.
A felületek metódusokat, tulajdonságokat, eseményeket és indexelőket tartalmazhatnak. Maga a felület nem biztosít implementációkat az általa deklarált tagok számára. Az interfész csupán azokat a tagokat határozza meg, amelyeket az interfészt megvalósító osztályok vagy szerkezetek szolgáltatnak.
18.2 Interfész deklarációi
18.2.1 Általános
A interface_declaration egy type_declaration (14.7.§), amely új interfésztípust deklarál.
interface_declaration
: attributes? interface_modifier* 'partial'? 'interface'
identifier variant_type_parameter_list? interface_base?
type_parameter_constraints_clause* interface_body ';'?
;
Az interface_declaration egy választható attribútumkészlet (22. §), amelyet egy választható interface_modifier-készlet (18.2.2. §) követ, majd egy választható részleges módosító (15.2.7. §), ezt követi a kulcsszó interface
és az interfészt nevesítő azonosító, majd egy opcionális variant_type_parameter_list specifikáció (18.2.3. §), majd egy választható interface_base specifikáció (18.2.4. §), amelyet egy opcionális type_parameter_constraints_clause specifikáció követ (15.2.5. §), majd egy interface_body (18.3. §), amelyet opcionálisan pontosvessző követi.
Egy interfész nyilatkozat nem szolgáltathat type_parameter_constraints_clauses-t, kivéve, ha egy variant_type_parameter_listis megadott.
Egy felületi deklaráció, amely biztosít egy variant_type_parameter_list-et, generikus felületi deklaráció. Ezenkívül az általános osztálydeklarációkba vagy általános szerkezetdeklarációkba ágyazott interfészek maguk is általános felületi deklarációk, mivel a típusargumentumokat a tárolótípus típusargumentumait kell megadni egy beépített típus létrehozásához (8.4. §).
18.2.2 Interfész módosítók
Egy interface_declaration opcionálisan tartalmazhat felületmódosítók sorozatát:
interface_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (23.2. §) csak nem biztonságos kódban érhető el (23. §).
Fordítási időhiba, hogy ugyanaz a módosító többször is megjelenik egy felületi deklarációban.
A new
módosító csak az osztályon belül definiált felületeken engedélyezett. Azt határozza meg, hogy a felület elrejti az azonos nevű örökölt elemet, ahogyan az a 15.3.5 szakaszban le van írva.
A public
, protected
, internal
és private
módosítók szabályozzák a felület akadálymentességét. Attól függően, hogy milyen környezetben történik az interfész deklarációja, ezen módosítók közül csak néhány engedélyezhető (7.5.2. §). Ha egy részleges típusdeklaráció (§15.2.7) tartalmaz egy hozzáférési specifikációt (a public
, protected
, internal
és private
módosítókon keresztül), a §15.2.2 szabályai érvényesek.
18.2.3 Változó típusú paraméterlisták
18.2.3.1 Általános
A variánstípus paraméterlistái csak interfész- és delegálási típusok esetén fordulhatnak elő. A különbség a szokásos típusparaméterlista-khez képest az egyes típusparaméterek opcionális variancia annotációja.
variant_type_parameter_list
: '<' variant_type_parameter (',' variant_type_parameter)* '>'
;
variant_type_parameter
: attributes? variance_annotation? type_parameter
;
variance_annotation
: 'in'
| 'out'
;
Ha a variancia széljegyzete azout
, a típusparaméter kovarianciánsnak minősül. Ha a variancia széljegyzete az in
, a típusparaméter állítólag contravariant. Ha nincs eltérési széljegyzet, a típusparaméter invariánsnak minősül.
Példa: Az alábbiakban:
interface C<out X, in Y, Z> { X M(Y y); Z P { get; set; } }
X
kovariáns,Y
kontravariáns ésZ
invariáns.záró példa
Ha egy általános interfész több részből áll (15.2.3. §), minden részleges deklarációnak ugyanazt a varianciát kell meghatároznia minden típusparaméterhez.
18.2.3.2 Varianciabiztonság
A variancia-megjegyzések előfordulása egy típus típusparaméter-listájában korlátozza azokat a helyeket, ahol a típusok a típusdeklarációban előfordulhatnak.
A kimenet-biztonságtalan típusú T esetén, ha az alábbiak egyike fennáll:
-
T
egy contravariant típusú paraméter -
T
egy olyan tömbtípus, amely kimenet szempontjából nem biztonságos elemtípussal rendelkezik -
T
egy interfész vagy delegáti típus, amely egy általános típusbólSᵢ,... Aₑ
lett létrehozvaS<Xᵢ, ... Xₑ>
, ahol az alábbi lehetőségek közül legalább egyAᵢ
teljesül:-
Xᵢ
kovariáns vagy invariáns, ésAᵢ
nem biztonságos a kimenete. -
Xᵢ
contravariant vagy invariáns, ésAᵢ
bemeneti-nem biztonságos.
-
A T típus bemenete nem biztonságos , ha az alábbiak egyike rendelkezik:
-
T
kovariant típusú paraméter -
T
olyan tömbtípus, amely bemeneti-nem biztonságos elemtípussal rendelkezik -
T
egy interfész vagy delegáti típus, amely egy általános típusbólS<Aᵢ,... Aₑ>
lett létrehozvaS<Xᵢ, ... Xₑ>
, ahol az alábbi lehetőségek közül legalább egyAᵢ
teljesül:-
Xᵢ
covariáns vagy invariáns, ésAᵢ
bemenet szempontjából nem biztonságos. -
Xᵢ
contravariant vagy invariáns, ésAᵢ
kimenete nem biztonságos.
-
Intuitív módon, a kimeneti pozícióban a kimeneti szempontból nem biztonságos típus tilos, és a bemeneti pozícióban a bemeneti szempontból nem biztonságos típus nem megengedett.
A típus kimenetbiztos, ha nem kimenet-nem biztonságos, és bemenet-biztonságos, ha nem bemeneti-nem biztonságos.
18.2.3.3 Variancia-átalakítás
A varianciajegyzetek célja, hogy megengedőbb (de mégis típusbiztos) átalakításokat biztosítson az interfész- és delegációs típusokra. E célból az implicit (10.2. §) és explicit konverziók (10.3. §) definíciói a variancia-konvertálhatóság fogalmát használják, amely a következőképpen van definiálva:
A típus T<Aᵢ, ..., Aᵥ>
variancia-átalakítható típussá T<Bᵢ, ..., Bᵥ>
, ha T
egy interfész vagy egy delegált típus, amelyet a variánstípus paramétereivel deklaráltak T<Xᵢ, ..., Xᵥ>
, és minden egyes változattípus-paraméterhez Xᵢ
az alábbiak egyike tartozik:
-
Xᵢ
kovariáns, és létezik egy implicit hivatkozás vagy identitásátalakításAᵢ
-bőlBᵢ
-be. -
Xᵢ
kontravariáns, és létezik egy implicit hivatkozás vagy identitás konverzióBᵢ
-rőlAᵢ
-re. invariáns, és van egy identitásátalakítás a -ból .
18.2.4 Alapfelületek
Az interfészek örökölhetnek nulla vagy több felülettípust, amelyeket az interfész explicit alapfelületeinek neveznek. Ha egy interfész egy vagy több explicit alapinterfésszel rendelkezik, akkor az interfész-azonosítót kettőspont és vesszővel tagolt alapinterfész-típusok listája következik.
interface_base
: ':' interface_type_list
;
Az explicit alapinterfészek felépíthetők interfésztípusok (§8.4, §18.2). Az alapfelület önmagában nem lehet típusparaméter, de magában foglalhatja a hatókörben lévő típusparamétereket is.
A létrehozott interfésztípusok esetében az explicit alapinterfészek úgy jönnek létre, hogy az általános típusdeklarációkon szereplő explicit alapinterfész deklarációit veszik alapul, és az alapinterfész-deklarációban szereplő type_parameter helyére a létrehozott típus megfelelő type_argument-ja kerül.
Az interfész explicit alapillesztőinek legalább olyan akadálymentesnek kell lenniük, mint maga az interfész (7.5.5. §).
Megjegyzés: Például fordítási hibát okoz, ha egy
private
vagyinternal
interfészt ad meg egy interface_base elemhez egypublic
interfészben. végjegyzet
Fordítási idejű hiba, ha egy interfész közvetlenül vagy közvetve örökli saját magától.
Az interfészek alapfelületei az explicit alapfelületek és azok alapfelületei. Más szóval, az alapinterfészek készlete az explicit alapinterfészek, azok explicit alapinterfészeinek teljes tranzitív lezárása és így tovább. Az interfész örökli az alapfelületek összes tagját.
Példa: Az alábbi kódban
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } interface IComboBox: ITextBox, IListBox {}
Az alapfelületek a
IComboBox
,IControl
,ITextBox
ésIListBox
. Más szóval, aIComboBox
fenti felület örökli a tagokatSetText
ésSetItems
, valamintPaint
is.záró példa
A létrehozott általános típusból öröklő tagok a típushelyettesítés után öröklődnek. Ez azt jelzi, hogy a tag összes összetevőtípusa az alaposztály-deklaráció típusparamétereit a class_base specifikációban használt megfelelő típusargumentumokra cseréli.
Példa: Az alábbi kódban
interface IBase<T> { T[] Combine(T a, T b); } interface IDerived : IBase<string[,]> { // Inherited: string[][,] Combine(string[,] a, string[,] b); }
a felület
IDerived
a típusparaméterCombine
lecserélése után örökli aT
metóduststring[,]
.záró példa
Az interfészt megvalósító osztály vagy szerkezet implicit módon implementálja az interfész összes alapillesztőjét is.
A részleges interfész deklaráció több részén található interfészek kezeléséről (15.2.7. §) részletesen a 15.2.4.3. fejezet tárgyalja.
Az illesztőfelület minden alapillesztőfelületének kimenetbiztosnak kell lennie (18.2.3.2. §).
18.3 Interfész törzse
Az interfész interface_body határozza meg a felület tagjait.
interface_body
: '{' interface_member_declaration* '}'
;
18.4 Felülettagok
18.4.1 Általános
Az interfészek tagjai az alapfelületektől örökölt tagok, és a felület által deklarált tagok.
interface_member_declaration
: interface_method_declaration
| interface_property_declaration
| interface_event_declaration
| interface_indexer_declaration
;
A felületi deklaráció nulla vagy több tagot deklarál. Az interfész tagjai módszerek, tulajdonságok, események vagy indexelők. Az interfészek nem tartalmazhatnak állandókat, mezőket, operátorokat, példánykonstruktorokat, véglegesítőket vagy típusokat, és nem tartalmazhatnak semmilyen statikus tagot.
Minden felületi tag implicit módon nyilvános hozzáféréssel rendelkezik. Fordítás közbeni hiba, ha az interfésztag-deklarációk tartalmaznak bármilyen módosítót.
Egy interface_declaration létrehoz egy új deklarációs területet (§7.3), és a típusparaméterek, valamint a interface_member_declaration-ok, amelyeket az interface_declaration közvetlen tartalmaz, új tagokat vezetnek be ebbe a deklarációs területbe. A következő szabályok vonatkoznak az interface_member_declaration-ekre:
- Az interfész-deklaráció variant_type_parameter_list egy típusparaméter neve eltér az ugyanazon variant_type_parameter_list található összes többi típusparaméter nevétől, és különbözik az interfész minden tagjának nevétől.
- A módszer neve eltér az azonos felületen deklarált összes tulajdonság és esemény nevétől. Ezenkívül egy módszer aláírásának különböznie kell az ugyanazon felületen deklarált összes többi módszer aláírásától, és az ugyanazon felületen deklarált két módszer nem rendelkezhet kizárólag ,
in
, ésout
különböző aláírással. - Egy tulajdonság vagy esemény neve eltér az azonos felületen bejelentett többi tag nevétől.
- Az indexelő aláírásának különböznie kell az azonos felületen bejelentett összes többi indexelő aláírásától.
Az interfész öröklött tagjai kifejezetten nem részei az interfész deklarációs terének. Így a felület lehetővé teszi, hogy deklaráljon egy olyan tagot, amelynek neve vagy aláírása megegyezik az örökölt tag nevével vagy aláírásával. Ha ez történik, a származtatott illesztőtag azt mondja, hogy elrejtse az alap illesztőtagot. Örökölt tag elrejtése nem tekinthető hibának, azonban a fordító figyelmeztetést ad ki emiatt. A figyelmeztetés mellőzése érdekében a származtatott interfész tag nyilatkozatának tartalmaznia kell egy new
módosítót, amely jelzi, hogy a származtatott tag célja az alaptag elrejtése. Ezt a témakört a 7.7.2.3 részletesebben megtárgyalásra kerül.
Ha egy new
módosító szerepel egy olyan deklarációban, amely nem rejt el öröklött tagot, a rendszer ennek megfelelően figyelmeztetést ad ki. Ezt a figyelmeztetést a módosító eltávolításával tiltja le a new
rendszer.
Megjegyzés: Az osztály
object
tagjai szigorúan nem tagjai semmilyen felületnek (18.4. §). Az osztályobject
tagjai azonban bármely felülettípusban elérhetőek a tagok keresésével (12.5. §). végjegyzet
A többrészesen deklarált felület tagjainak halmaza (15.2.7. §) az egyes részekben deklarált tagok egysége. Az interfész-nyilatkozat minden részének szervei azonos deklarációs térrel (7.3. §) osztoznak, és az egyes tagok hatóköre (7.7. §) az összes rész testületére kiterjed.
18.4.2 Interfész metódusok
A felületi metódusok deklarálása interface_method_declarationhasználatával történik:
interface_method_declaration
: attributes? 'new'? return_type interface_method_header
| attributes? 'new'? ref_kind ref_return_type interface_method_header
;
interface_method_header
: identifier '(' parameter_list? ')' ';'
| identifier type_parameter_list '(' parameter_list? ')'
type_parameter_constraints_clause* ';'
;
Az interfészmetódus-deklarációk attribútumai, return_type, ref_return_type, azonosítója és parameter_list jelentése megegyezik az osztályban lévő metódusdeklarációkéval (15.6. §). Az interfészmetódus-deklaráció nem adhat meg metódustörzset, ezért a deklaráció mindig pontosvesszővel végződik.
Az interfészmetódus minden paramétertípusának bemenetbiztosnak kell lennie (18.2.3.2. §), és a visszatérési típusnak vagy void
kimenetbiztosnak kell lennie. Ezenkívül minden kimeneti vagy referenciaparaméter-típusnak kimenetbiztosnak kell lennie.
Megjegyzés: A kimeneti paramétereknek bemenetbiztosnak kell lenniük a gyakori megvalósítási korlátozások miatt. végjegyzet
Ezenkívül minden osztálytípus-korlátozásnak, interfésztípus-korlátozásnak és típusparaméter-kényszernek a módszer bármely típusparaméterére vonatkozóan bemenetbiztosnak kell lennie.
Ezenkívül minden osztálytípus-korlátozásnak, interfésztípus-megkötésnek és típusparaméter-kényszernek a módszer bármely típusparaméterén bemenetbiztosnak kell lennie.
Ezek a szabályok biztosítják, hogy az interfész kovariáns vagy kontravariáns használata típusbiztos maradjon.
Példa:
interface I<out T> { void M<U>() where U : T; // Error }
A
T
típusparaméter-korlátozás aU
-en történő használata hibás, mert nem biztonságos bemenetként.Ha ez a korlátozás nincs érvényben, a típusbiztonságot a következő módon lehet megsérteni:
class B {} class D : B {} class E : B {} class C : I<D> { public void M<U>() {...} } ... I<B> b = new C(); b.M<E>();
Ez valójában egy hívás
C.M<E>
-ra. De ez a hívás megköveteli, hogy aE
aD
-ből származzon, így a típusbiztonság sérülne itt.záró példa
18.4.3 Interfész tulajdonságai
Az illesztőtulajdonságokat interface_property_declaration-okkal deklarálják.
interface_property_declaration
: attributes? 'new'? type identifier '{' interface_accessors '}'
| attributes? 'new'? ref_kind type identifier '{' ref_interface_accessor '}'
;
interface_accessors
: attributes? 'get' ';'
| attributes? 'set' ';'
| attributes? 'get' ';' attributes? 'set' ';'
| attributes? 'set' ';' attributes? 'get' ';'
;
ref_interface_accessor
: attributes? 'get' ';'
;
Az interfésztulajdonság-deklaráció attribútumainak, típusának és azonosítójának jelentése megegyezik egy osztály tulajdonságdeklarációjának jelentésével (15.7. §).
Az interfésztulajdonság-deklaráció tartozékai megfelelnek az osztálytulajdonság-deklaráció tartozékainak (15.7.3. §), azzal a kivételével, hogy a accessor_body mindig pontosvesszővel kell rendelkezniük. Így a hozzáférők egyszerűen jelzik, hogy a tulajdonság olvasható és írható, csak olvasható, vagy csak írható.
Az illesztőtulajdonság típusának kimenetbiztosnak kell lennie, ha van beszerelhető tartozék, és bemenetbiztosnak kell lennie, ha van beállítva tartozék.
18.4.4 Interfészesemények
Az interfészesemények az interface_event_declaration deklarálásával vannak megadva.
interface_event_declaration
: attributes? 'new'? 'event' type identifier ';'
;
Az interfészesemény-deklaráció attribútumai, típusa és azonosítója megegyezik az osztály eseménydeklarációinak jelentésével (15.8. §).
Az interfészesemény típusának bemenetbiztosnak kell lennie.
18.4.5 Felületindexelők
A felületindexelők interface_indexer_declarationekkel vannak deklarálva:
interface_indexer_declaration
: attributes? 'new'? type 'this' '[' parameter_list ']'
'{' interface_accessors '}'
| attributes? 'new'? ref_kind type 'this' '[' parameter_list ']'
'{' ref_interface_accessor '}'
;
Az illesztőindex-deklaráció attribútumai, típusa és parameter_list ugyanazzal a jelentéssel rendelkeznek, mint egy indexelő deklarációé egy osztályban (15.9. §).
Az interfész indexelő deklaráció hozzáférői megfelelnek az osztály indexelő deklarációjának hozzáférőinek (15.9. §), azzal a kitétellel, hogy a accessor_body mindig pontosvessző legyen. Így az elérési metódusok egyszerűen jelzik, hogy az indexelő olvasható és írható, csak olvasható, vagy csak írható.
Az illesztőindexelő minden paramétertípusának bemenetbiztosnak kell lennie (18.2.3.2. §). Ezenkívül minden kimeneti vagy referenciaparaméter-típusnak kimenetbiztosnak kell lennie.
Megjegyzés: A kimeneti paramétereknek bemenetbiztosnak kell lenniük a gyakori megvalósítási korlátozások miatt. végjegyzet
Az interface indexelő típusának kimenetbiztosnak kell lennie, ha van get hozzáférő, és bemenetbiztosnak kell lennie, ha van set hozzáférő.
18.4.6 Felülettag-hozzáférés
A felülettagok hozzáférése (§12.8.7) és indexelő hozzáféréssel (§12.8.12.3) az űrlap I.M
és I[A]
kifejezésével érhető el, ahol a I
felülettípus, M
az adott felülettípus metódusa, tulajdonsága vagy eseménye, A
pedig indexelő argumentumlista.
A szigorúan egyszeres öröklésű felületek esetén (az öröklési lánc minden felülete pontosan nulla vagy egy közvetlen alapfelülettel rendelkezik), a tagkeresés (§12.5), a metódushívás (§12.8.10.2) és az indexelő hozzáférés (§12.8.12.3) szabályai pontosan megegyeznek az osztályok és a struktúrák esetében: A jobban származtatott tagok elrejtik a kevésbé származtatott tagokat ugyanazzal a névvel vagy aláírással. A többöröklésű felületek esetében azonban kétértelműség fordulhat elő, ha két vagy több nem kapcsolódó alapfelület azonos nevű vagy aláírású tagokat deklarál. Ez az alcím számos példát mutat be, amelyek közül néhány kétértelműséghez vezet, mások pedig nem. A kétértelműségek megoldásához minden esetben explicit szereplők használhatók.
Példa: Az alábbi kódban
interface IList { int Count { get; set; } } interface ICounter { int Count { get; set; } } interface IListCounter : IList, ICounter {} class C { void Test(IListCounter x) { x.Count = 1; // Error ((IList)x).Count = 1; // Ok, invokes IList.Count.set ((ICounter)x).Count = 1; // Ok, invokes ICounter.Count } }
az első utasítás fordítási időbeli hibát okoz, mert a tag keresése
Count
-ben (IListCounter
) nem egyértelmű. Ahogy a példában látható, a kétértelműséget azx
megfelelő alapinterfész típusra történő átalakításával lehet megoldani. Az ilyen kasztoknak nincs futásidejű költségük – csupán abból állnak, hogy fordításkor a példányt kevésbé specifikus típusként kezelik.záró példa
Példa: Az alábbi kódban
interface IInteger { void Add(int i); } interface IDouble { void Add(double d); } interface INumber : IInteger, IDouble {} class C { void Test(INumber n) { n.Add(1); // Invokes IInteger.Add n.Add(1.0); // Only IDouble.Add is applicable ((IInteger)n).Add(1); // Only IInteger.Add is a candidate ((IDouble)n).Add(1); // Only IDouble.Add is a candidate } }
a
n.Add(1)
meghívás a túlterhelés feloldási szabályainak alkalmazásával választja ki aIInteger.Add
a §12.6.4 szerint. Hasonlóképpen, a hívásn.Add(1.0)
kiválasztjaIDouble.Add
. Explicit leadások beszúrásakor csak egy jelöltmetódus van, így nincs kétértelműség.záró példa
Példa: Az alábbi kódban
interface IBase { void F(int i); } interface ILeft : IBase { new void F(int i); } interface IRight : IBase { void G(); } interface IDerived : ILeft, IRight {} class A { void Test(IDerived d) { d.F(1); // Invokes ILeft.F ((IBase)d).F(1); // Invokes IBase.F ((ILeft)d).F(1); // Invokes ILeft.F ((IRight)d).F(1); // Invokes IBase.F } }
a
IBase.F
tag el van rejtve aILeft.F
tag által. Ad.F(1)
hívása ígyILeft.F
-t választja, bárIBase.F
úgy tűnik, hogy nincs elrejtve azIRight
-en keresztül vezető elérési úton.A többöröklésű felületeken való elrejtés intuitív szabálya egyszerűen a következő: Ha egy tag bármely elérési útvonalon el van rejtve, az minden elérési útvonalon el van rejtve. Mivel a hozzáférési útvonal
IDerived
ésILeft
között elrejtiIBase
-t, a tag szintén el van rejtve a hozzáférési útvonalbanIBase.F
ésIDerived
között.záró példa
18.5 A felület minősített tagnevei
Az illesztőtagot néha minősített illesztőtag néven emlegetik. Az interfész tag minősített neve azon interfész neve, amelyben a tag deklarálva van, ezt egy pont követi, majd a tag neve. A tag minősített neve arra a felületre hivatkozik, amelyben a tag deklarálva van.
Példa: Adott deklarációk esetén
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); }
A(z)
Paint
minősített neveIControl.Paint
, és a(z) SetText minősített neveITextBox.SetText
. A fenti példában nem lehet hivatkozni a következőrePaint
ITextBox.Paint
: .záró példa
Ha egy felület egy névtér része, a minősített illesztőtagok neve magában foglalhatja a névtér nevét.
Példa:
namespace System { public interface ICloneable { object Clone(); } }
System
névtéren belül mind aICloneable.Clone
, mind aSystem.ICloneable.Clone
aClone
metódushoz tartozó interfész tagnevek.záró példa
18.6 Interfész implementációi
18.6.1 Általános
A felületeket osztályok és szerkezetek implementálhatják. Annak jelzéséhez, hogy egy osztály vagy strustruktúra közvetlenül implementál egy interfészt, az interfész szerepel az osztály vagy a szerkezet alaposztálylistájában.
Példa:
interface ICloneable { object Clone(); } interface IComparable { int CompareTo(object other); } class ListEntry : ICloneable, IComparable { public object Clone() {...} public int CompareTo(object other) {...} }
záró példa
Az interfészt közvetlenül megvalósító osztály vagy struktúra implicit módon implementálja az interfész összes alapillesztőjét is. Ez akkor is igaz, ha az osztály vagy a szerkezet nem sorolja fel explicit módon az alaposztálylistában szereplő összes alapillesztőt.
Példa:
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { public void Paint() {...} public void SetText(string text) {...} }
Itt a
TextBox
osztály mind aIControl
, mind aITextBox
-t megvalósítja.záró példa
Amikor egy osztály C
közvetlenül implementál egy felületet, az összes származtatott C
osztály implicit módon is implementálja az interfészt.
Az osztálydeklarációban megadott alapillesztők lehetnek felépített illesztőtípusok (§8.4, §18.2).
Példa: Az alábbi kód bemutatja, hogyan valósíthat meg egy osztály létrehozott felülettípusokat:
class C<U, V> {} interface I1<V> {} class D : C<string, int>, I1<string> {} class E<T> : C<int, T>, I1<T> {}
záró példa
Az általános osztály deklaráció alapfelületeinek meg kell felelniük az §18.6.3-ban leírt egyediség szabályának.
18.6.2 Az interfész tagok explicit implementációi
Interfészek megvalósítása céljából egy osztály vagy strustruktúra explicit felülettag-implementációkat deklarálhat. Az explicit felülettag-implementáció egy metódus, tulajdonság, esemény vagy indexelő deklaráció, amely egy minősített felülettag nevére hivatkozik.
Példa:
interface IList<T> { T[] GetElements(); } interface IDictionary<K, V> { V this[K key] { get; } void Add(K key, V value); } class List<T> : IList<T>, IDictionary<int, T> { public T[] GetElements() {...} T IDictionary<int, T>.this[int index] {...} void IDictionary<int, T>.Add(int index, T value) {...} }
Itt található a
IDictionary<int,T>.this
ésIDictionary<int,T>.Add
explicit felülettag-implementációk.záró példa
Példa: Bizonyos esetekben előfordulhat, hogy az illesztőtag neve nem felel meg a implementálási osztálynak, ebben az esetben a felülettag explicit felülettag-implementációval implementálható. Egy fájl absztrakciót megvalósító osztály például valószínűleg implementál egy
Close
tagfüggvényt, amely felszabadítja a fájlerőforrást, és explicit interfész tagfüggvény implementációval valósítja meg azDispose
metódusát azIDisposable
interfésznek.interface IDisposable { void Dispose(); } class MyFile : IDisposable { void IDisposable.Dispose() => Close(); public void Close() { // Do what's necessary to close the file System.GC.SuppressFinalize(this); } }
záró példa
Az explicit felülettag-implementáció nem érhető el a megfelelő illesztőtag nevével egy metódushívásban, tulajdonsághozzáférésben, eseményhozzáférésben vagy indexelő hozzáférésben. Az explicit felülettag-implementáció csak egy felületpéldányon keresztül érhető el, és ebben az esetben egyszerűen a tag neve hivatkozik rá.
Fordítási idejű hiba, ha egy explicit felülettag implementációja bármilyen módosítót tartalmaz az vagy extern
kivételével.
Explicit interfész metódus implementációjának fordítási idejű hibája, ha type_parameter_constraints_clause elemeket tartalmaz. Az általános explicit felületi metódus implementálásának korlátait a rendszer a felületmetódustól örökli.
Megjegyzés: Az explicit interfész tagok implementációi más hozzáférhetőségi jellemzőkkel rendelkeznek, mint a többi tag. Mivel az explicit felülettag-implementációk soha nem érhetők el egy metódushívásban vagy tulajdonsághozzáférésben lévő minősített illesztőtagnéven keresztül, bizonyos értelemben privátak. Mivel azonban az interfészen keresztül is elérhetők, olyan értelemben is nyilvánosak, mint az a felület, amelyben deklarálva vannak. Az explicit felülettag-implementációk két elsődleges célt szolgálnak:
- Mivel az explicit illesztőtag-implementációk nem érhetők el osztály- vagy struktúrapéldányokon keresztül, lehetővé teszik, hogy a felületi implementációk ki legyenek zárva egy osztály vagy struktúra nyilvános felületéről. Ez különösen akkor hasznos, ha egy osztály vagy strustruktúra olyan belső felületet implementál, amely nem érdekli az adott osztály vagy szerkezet fogyasztóját.
- Az explicit felülettag-implementációk lehetővé teszik az azonos aláírással rendelkező felülettagok egyértelműsítését. Explicit felülettag-implementációk nélkül lehetetlen lenne, hogy egy osztály vagy strustruktúra azonos aláírással és visszatérési típussal rendelkezzen az illesztőtagok különböző implementációival, mivel egy osztály vagy strustruktúra esetében lehetetlen lenne bármilyen implementációt végrehajtani az azonos aláírású, de eltérő visszatérési típusok esetén.
végjegyzet
Ahhoz, hogy az explicit felülettag-implementáció érvényes legyen, az osztálynak vagy a szerkezetnek el kell neveznie egy interfészt az alaposztálylistájában, amely egy olyan tagot tartalmaz, akinek a megfelelő felülettag neve, típusa, típusparamétereinek száma és paramétertípusai pontosan egyeznek az explicit felülettag-implementációéval. Ha egy interfészfüggvény-tag rendelkezik paramétertömbbel, a társított explicit felülettag-implementáció megfelelő paramétere engedélyezett, de nem kötelező, hogy rendelkezzen a params
módosítóval. Ha az interfészfüggvény-tag nem rendelkezik paramétertömbbel, akkor a társított explicit illesztőtag-implementáció nem rendelkezik paramétertömbbel.
Példa: Például a következő osztályban
class Shape : ICloneable { object ICloneable.Clone() {...} int IComparable.CompareTo(object other) {...} // invalid }
A
IComparable.CompareTo
deklarációja fordítási idejű hibát eredményez, mertIComparable
nem szerepel aShape
alaposztályainak listájában, és nem alapinterfésze aICloneable
-nak. Hasonlóképpen, a deklarációkbanclass Shape : ICloneable { object ICloneable.Clone() {...} } class Ellipse : Shape { object ICloneable.Clone() {...} // invalid }
ICloneable.Clone
deklarálásaEllipse
fordítási hibát eredményez, mertICloneable
nincs kifejezetten felsorolvaEllipse
alaposztálylistájában.záró példa
Az explicit interfész tagjának kvalifikált tagneve arra az interfészre hivatkozik, amelyben a tagot deklarálták.
Példa: Így a deklarációkban
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} }
A Paint explicit felülettag-implementációját
IControl.Paint
formájában kell írni, nemITextBox.Paint
.záró példa
18.6.3 A megvalósított interfészek egyedisége
Az általános típusdeklarációval megvalósított interfészeknek minden lehetséges felépített típus esetében egyedinek kell maradniuk. E szabály nélkül lehetetlen lenne meghatározni a megfelelő metódust bizonyos létrehozott típusok meghívásához.
Példa: Tegyük fel, hogy egy általános osztály deklarációja a következőképpen írható:
interface I<T> { void F(); } class X<U ,V> : I<U>, I<V> // Error: I<U> and I<V> conflict { void I<U>.F() {...} void I<V>.F() {...} }
Ha ez engedélyezve lenne, lehetetlen lenne meghatározni, hogy a következő esetben melyik kódot kell végrehajtani:
I<int> x = new X<int, int>(); x.F();
záró példa
Annak megállapításához, hogy egy általános típusú deklaráció felületlistája érvényes-e, a következő lépéseket kell végrehajtani:
- Legyen
L
az általános osztályban, szerkezetben vagy interfészdeklarációbanC
közvetlenül megadott interfészek listája. - Adja hozzá a
L
elemekhez aL
már meglévő illesztőinek bármely alapillesztőjét. - Távolítsa el az ismétlődéseket a
L
-ból. - Ha a típusargumentumok
C
-be való behelyettesítése után bármely lehetséges konstruált típus két azonos interfészhez vezetL
-ben, akkor aL
deklarációja érvénytelen. A kényszer deklarációk nem tekinthetők meg az összes lehetséges felépített típus meghatározásakor.
Megjegyzés: A fenti osztálydeklarációban
X
a felületlistaL
a következőkből álll<U>
: ésI<V>
. A deklaráció érvénytelen, mert bármely összefércelés, aholU
ésV
ugyanaz a típus, ezt a két illesztőfelületet azonos típusokká tenné. végjegyzet
A különböző öröklési szinteken megadott interfészek egyesíthetik az alábbiakat:
interface I<T>
{
void F();
}
class Base<U> : I<U>
{
void I<U>.F() {...}
}
class Derived<U, V> : Base<U>, I<V> // Ok
{
void I<V>.F() {...}
}
Ez a kód akkor is érvényes, ha Derived<U,V>
mind I<U>
, mind I<V>
megvalósít. A kód
I<int> x = new Derived<int, int>();
x.F();
Meghívja a metódust Derived
, mivel Derived<int,int>'
hatékonyan újra implementálja I<int>
-t (§18.6.7).
18.6.4 Általános módszerek alkalmazása
Amikor egy általános módszer implicit módon implementál egy interfészmetódust, az egyes metódustípus-paraméterekre vonatkozó korlátozásoknak egyenértékűnek kell lenniük mindkét deklarációban (miután az interfésztípus paramétereit lecserélték a megfelelő típusargumentumokra), ahol a metódustípus paramétereit a balról jobbra lévő sorszámok azonosítják.
Példa: A következő kódban:
interface I<X, Y, Z> { void F<T>(T t) where T : X; void G<T>(T t) where T : Y; void H<T>(T t) where T : Z; } class C : I<object, C, string> { public void F<T>(T t) {...} // Ok public void G<T>(T t) where T : C {...} // Ok public void H<T>(T t) where T : string {...} // Error }
a módszer
C.F<T>
implicit módon implementálI<object,C,string>.F<T>
. Ebben az esetben nincs szükség (és nem is engedélyezett) a korlátozásC.F<T>
megadására,T: object
mivelobject
az minden típusparaméter implicit kényszere. A metódusC.G<T>
implicit módon implementálI<object,C,string>.G<T>
, mert a kényszerek megegyeznek az illesztőben lévőkkel, miután az illesztőtípus paramétereit lecserélte a megfelelő típusargumentumokra. A metódusC.H<T>
kényszere hiba, mert a lezárt típusok (string
ebben az esetben) nem használhatók kényszerként. A kényszer kihagyása szintén hiba lenne, mivel az implicit interfészmetódus implementációinak megkötéseinek meg kell egyeznie. Így lehetetlen implicit módon implementálniI<object,C,string>.H<T>
. Ez az interfészmetódus csak explicit felülettag-implementációval implementálható:class C : I<object, C, string> { ... public void H<U>(U u) where U : class {...} void I<object, C, string>.H<T>(T t) { string s = t; // Ok H<T>(t); } }
Ebben az esetben az explicit felülettag-implementáció egy olyan nyilvános metódust hív meg, amely szigorúan gyengébb korlátozásokkal rendelkezik. A t-ből s-be való hozzárendelés érvényes, mivel
T
örökli a kényszertT: string
, annak ellenére, hogy ez a kényszer nem fejezhető ki a forráskódban. záró példa
Megjegyzés: Ha egy általános módszer explicit módon implementál egy interfészmetódust, a implementálási módszerre nincs korlátozás (15.7.1. §, 18.6.2. §). végjegyzet
18.6.5 Felületleképezés
Az osztálynak vagy a szerkezetnek biztosítania kell az interfészek minden olyan tagjának implementációját, amely szerepel az osztály vagy a szerkezet alaposztálylistájában. Az interfésztagok implementációinak implementálási folyamata egy implementációs osztályban vagy strustruktúra esetében interfészleképezésnek nevezik.
Egy osztály vagy struktúra C
interfész-leképezése megkeresi az implementációt az egyes interfészek minden tagjához, amely az alaposztálylistában van megadva C
. Egy adott interfésztag I.M
implementálása – ahol I
az az interfész, amelyben a tagot M
deklarálták – az egyes osztályok vagy struktúrák S
felülvizsgálatával van meghatározva, kezdve a C
osztállyal vagy struktúrával, és ezt minden egyes egymást követő alaposztály C
esetében megismételve, amíg egyezésre nem bukkanunk:
- Ha a
S
deklarál egy explicit felülettag-implementációt, amely megfelel aI
ésM
tagoknak, akkor ez a tag aI.M
implementációja. - Ellenkező esetben, ha a
S
egy nem statikus nyilvános tag számára tartalmaz deklarációt, amely megfelel aM
-nek, akkor ez a tag aI.M
megvalósítása. Ha egynél több tag egyezik, nincs meghatározva, hogy melyik tag az implementációjaI.M
. Ez a helyzet csak akkor fordulhat elő, haS
egy olyan létrehozott típus, amelyben az általános típusban deklarált két tag eltérő aláírással rendelkezik, de a típusargumentumok azonosvá teszik az aláírásukat.
Fordítási időhiba lép fel, ha a implementációk nem találhatók a rendszer alaposztálylistájában megadott összes felület minden tagjára C
vonatkozóan. Az interfész tagjai közé tartoznak azok a tagok, amelyek az alapinterfészekből öröklődnek.
A létrehozott interfésztípus tagjait úgy tekintjük, hogy az egyes típusparamétereket az §15.3.3-ben megadott megfelelő típusargumentumokkal helyettesítik.
Példa: Például az általános felületi deklaráció miatt:
interface I<T> { T F(int x, T[,] y); T this[int y] { get; } }
a létrehozott felület
I<string[]>
tagjai:string[] F(int x, string[,][] y); string[] this[int y] { get; }
záró példa
Felületleképezés céljából egy osztály vagy strucctag A
akkor egyezik meg egy illesztőtagval B
, ha:
-
A
ésB
metódusok, ésA
ésB
neve, típusa és paraméterlistája azonos. -
A
ésB
tulajdonságok, a név és a típusA
, ésB
azonosak, ésA
ugyanazokkal a tartozékokkal rendelkezik, mintB
(A
megengedett, hogy további tartozékokkal rendelkezzen, ha nem explicit felületi tag implementáció). -
A
ésB
események, és aA
ésB
neve és típusa azonos. -
A
ésB
indexelők, aA
ésB
típus- és paraméterlistái azonosak, ésA
ugyanazokkal a hozzáférési metódusokkal rendelkezik, mintB
(A
további hozzáférési metódusokkal is rendelkezhet, ha nem explicit interfész-tag implementáció).
Az interfész-leképezési algoritmus jelentős következményei a következők:
- Az explicit felülettag-implementációk elsőbbséget élveznek az ugyanabban az osztályban vagy szerkezetben lévő többi taggal szemben az illesztőtagot megvalósító osztály vagy tagstruktúra meghatározásakor.
- Sem a nem nyilvános, sem a statikus tagok nem vesznek részt a felületleképezésben.
Példa: Az alábbi kódban
interface ICloneable { object Clone(); } class C : ICloneable { object ICloneable.Clone() {...} public object Clone() {...} }
A(z)
ICloneable.Clone
tagja a(z)C
-ban a(z)Clone
megvalósításává válik a(z)ICloneable
-ban, mivel az explicit felülettag-implementációk elsőbbséget élveznek más tagokkal szemben.záró példa
Ha egy osztály vagy szerkezet két vagy több olyan illesztőt implementál, amely azonos nevű, típusú és paramétertípusú tagot tartalmaz, az illesztőtagok mindegyike megfeleltethető egyetlen osztályra vagy strukturált tagra.
Példa:
interface IControl { void Paint(); } interface IForm { void Paint(); } class Page : IControl, IForm { public void Paint() {...} }
Itt mind a
Paint
, mind aIControl
módszerei aIForm
metódushoz lettek megfeleltetve aPaint
-ben. Természetesen külön explicit felülettag-implementációk is lehetségesek a két módszerhez.záró példa
Ha egy osztály vagy strustruktúra rejtett tagokat tartalmazó felületet implementál, akkor előfordulhat, hogy egyes tagokat explicit felületi tag-implementációkkal kell implementálni.
Példa:
interface IBase { int P { get; } } interface IDerived : IBase { new int P(); }
Ennek az interfésznek a megvalósítása legalább egy explicit felületi tag implementálását igényelné, és az alábbi űrlapok egyikét venné igénybe
class C1 : IDerived { int IBase.P { get; } int IDerived.P() {...} } class C2 : IDerived { public int P { get; } int IDerived.P() {...} } class C3 : IDerived { int IBase.P { get; } public int P() {...} }
záró példa
Ha egy osztály több, azonos alapfelülettel rendelkező adaptert implementál, az alapillesztőnek csak egy implementációja lehet.
Példa: Az alábbi kódban
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } class ComboBox : IControl, ITextBox, IListBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} void IListBox.SetItems(string[] items) {...} }
nem lehet külön implementációkat létrehozni az
IControl
alaposztálylistában szereplő névhez, azIControl
által örököltITextBox
, és azIControl
által örököltIListBox
. Ezen interfészek esetében nincs külön identitás fogalma. Ehelyett aITextBox
és aIListBox
ugyanazt aIControl
implementációt osztja meg, és aComboBox
egyszerűen úgy tekinthető, mint amely három interfészt valósít meg:IControl
,ITextBox
, ésIListBox
.záró példa
Az alaposztály tagjai részt vesznek a felületleképezésben.
Példa: Az alábbi kódban
interface Interface1 { void F(); } class Class1 { public void F() {} public void G() {} } class Class2 : Class1, Interface1 { public new void G() {} }
A
F
módszert aClass1
aClass2's
rendszer implementációjában használjákInterface1
.záró példa
18.6.6 Interfész implementálásának öröklése
Az osztály örökli az alaposztályai által biztosított összes felületi implementációt.
Az interfész explicit újra implementálása nélkül a származtatott osztály semmilyen módon nem módosíthatja az alaposztályoktól örökölt interfész-leképezéseket.
Példa: A deklarációkban
interface IControl { void Paint(); } class Control : IControl { public void Paint() {...} } class TextBox : Control { public new void Paint() {...} }
a
Paint
metódus aTextBox
-ban elrejti aPaint
metódust aControl
-ban, de nem módosítja aControl.Paint
leképezését azIControl.Paint
-re, és az osztálypéldányokon és felületpéldányokon keresztüliPaint
hívások a következő hatásokkal járnakControl c = new Control(); TextBox t = new TextBox(); IControl ic = c; IControl it = t; c.Paint(); // invokes Control.Paint(); t.Paint(); // invokes TextBox.Paint(); ic.Paint(); // invokes Control.Paint(); it.Paint(); // invokes Control.Paint();
záró példa
Ha azonban egy interfészmetódus egy osztály virtuális metódusára van leképezve, lehetséges, hogy a származtatott osztályok felülbírálják a virtuális metódust, és módosítják a felület implementációját.
Példa: A fenti deklarációk újraírása a következőre:
interface IControl { void Paint(); } class Control : IControl { public virtual void Paint() {...} } class TextBox : Control { public override void Paint() {...} }
a következő hatások figyelhetők meg
Control c = new Control(); TextBox t = new TextBox(); IControl ic = c; IControl it = t; c.Paint(); // invokes Control.Paint(); t.Paint(); // invokes TextBox.Paint(); ic.Paint(); // invokes Control.Paint(); it.Paint(); // invokes TextBox.Paint();
záró példa
Mivel az explicit felülettag-implementációk nem deklarálhatók virtuálisan, nem lehet felülbírálni egy explicit felülettag-implementációt. Az explicit felülettag implementációja azonban teljesen érvényes arra, hogy meghívjon egy másik metódust, és ezt a másik metódust lehet virtuálisan deklarálni, hogy a származtatott osztályok felüldefiniálhassák azt.
Példa:
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() { PaintControl(); } protected virtual void PaintControl() {...} } class TextBox : Control { protected override void PaintControl() {...} }
Itt a
Control
osztályokból származtatott osztályok aIControl.Paint
metódus felülbírálásával specializálhatják aPaintControl
végrehajtását.záró példa
18.6.7 Interfész újra implementálása
Az interfész implementálását öröklő osztály újra implementálhatja a felületet úgy, hogy felveszi az alaposztálylistába.
Az interfészek újra implementálása pontosan ugyanazokat a felületleképezési szabályokat követi, mint egy felület kezdeti implementációja. Így az örökölt felületleképezés semmilyen hatással nincs a felület újra implementálására létrehozott felületleképezésre.
Példa: A deklarációkban
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() {...} } class MyControl : Control, IControl { public void Paint() {} }
az a tény, hogy
Control
-tIControl.Paint
-re leképeziControl.IControl.Paint
-t, nem befolyásoljaMyControl
újramegvalósítását, amiIControl.Paint
-tMyControl.Paint
-re leképez.záró példa
Az örökölt nyilvános tag-deklarációk és az örökölt explicit felülettag-deklarációk részt vesznek az újra implementált felületek felületleképezési folyamatában.
Példa:
interface IMethods { void F(); void G(); void H(); void I(); } class Base : IMethods { void IMethods.F() {} void IMethods.G() {} public void H() {} public void I() {} } class Derived : Base, IMethods { public void F() {} void IMethods.H() {} }
Itt az implementáció leképezi az interfész metódusait
IMethods
Derived
azDerived.F
,Base.IMethods.G
,Derived.IMethods.H
ésBase.I
elemekre.záró példa
Amikor egy osztály implementál egy interfészt, implicit módon az interfész összes alapfelületét is implementálja. Hasonlóképpen, az interfész újra implementálása implicit módon az interfész összes alapfelületének újra implementálása is.
Példa:
interface IBase { void F(); } interface IDerived : IBase { void G(); } class C : IDerived { void IBase.F() {...} void IDerived.G() {...} } class D : C, IDerived { public void F() {...} public void G() {...} }
Itt a
IDerived
újraimplementálása is újraimplementálja aIBase
-t, hozzárendelve aIBase.F
-t aD.F
-hoz.záró példa
18.6.8 Absztrakt osztályok és felületek
A nem absztrakciós osztályhoz hasonlóan az absztrakt osztálynak az osztály alaposztálylistájában felsorolt interfészek összes tagjának implementációit kell biztosítania. Az absztrakt osztályok azonban interfész metódusokat képezhetnek le absztrakt metódusokra.
Példa:
interface IMethods { void F(); void G(); } abstract class C : IMethods { public abstract void F(); public abstract void G(); }
Itt a
IMethods
megvalósítása aF
ésG
elemeket absztrakt metódusokra képezi le, amelyeket felül kell bírálni azokban a nem absztrakt osztályokban, amelyek aC
-ből származnak.záró példa
Az explicit felülettag-implementációk nem lehetnek absztraktak, de az explicit felülettag-implementációk természetesen absztrakt metódusokat is meghívhatnak.
Példa:
interface IMethods { void F(); void G(); } abstract class C: IMethods { void IMethods.F() { FF(); } void IMethods.G() { GG(); } protected abstract void FF(); protected abstract void GG(); }
Itt a
C
-ből származó nem absztrakt osztályoknak felül kell bírálniuk aFF
-t és aGG
-t, így biztosítva ezzel aIMethods
tényleges megvalósítását.záró példa
ECMA C# draft specification