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.
A Felügyelt bővíthetőségi keretrendszerben (MEF) egy programozási modell határozza meg azokat a fogalmi objektumokat, amelyeken a MEF működik. Ezek a fogalmi objektumok közé tartoznak az alkatrészek, az importálások és az exportálások. A MEF ezeket az objektumokat használja, de nem határozza meg, hogyan kell őket ábrázolni. Ezért számos programozási modell lehetséges, beleértve a testreszabott programozási modelleket is.
A MEF-ben használt alapértelmezett programozási modell az attribútumként megadott programozási modell. Az attribútumos programozási modell részeiben az importálás, az exportálás és más objektumok olyan attribútumokkal vannak meghatározva, amelyek a szokásos .NET osztályokat díszítik. Ez a cikk bemutatja, hogyan használható az attribútumokkal rendelkező programozási modell által biztosított attribútumok MEF-alkalmazás létrehozásához.
Az importálás és exportálás alapjai
Az export egy érték, amelyet egy alkatrész biztosít a tároló más részeinek, és az import olyan követelmény, amelyet egy rész kifejez a tároló felé, és amelyet az elérhető exportokból kell ellátni. Az attribútumos programozási modellben az importálást és exportálást úgy deklarálják, hogy osztályokat vagy tagokat Import és Export attribútumokkal látnak el. Az Export attribútum egy osztályt, mezőt, tulajdonságot vagy metódust díszíthet, míg az Import attribútum díszíthet egy mezőt, tulajdonságot vagy konstruktorparamétert.
Ahhoz, hogy az importálást exporttal lehessen egyeztetni, az importálásnak és az exportálásnak ugyanazzal a szerződéssel kell rendelkeznie. A szerződés egy sztringből, az úgynevezett szerződésnévből és az exportált vagy importált objektum típusából, úgynevezett szerződéstípusból áll. Csak akkor tekinthető egy export egy adott import teljesítésének, ha a szerződés neve és típusa megegyezik.
Vagy mindkét szerződésparaméter implicit vagy explicit lehet. Az alábbi kód egy alapszintű importálást deklaráló osztályt mutat be.
Public Class MyClass1
<Import()>
Public Property MyAddin As IMyAddin
End Class
public class MyClass
{
[Import]
public IMyAddin MyAddin { get; set; }
}
Ebben az importálásban az Import attribútum nem rendelkezik sem szerződéstípussal, sem szerződésnévparaméterrel. Ezért mindkettőt a díszített tulajdonságból kell kikövetkeztetni. Ebben az esetben a szerződés típusa lesz IMyAddin, a szerződés neve pedig a szerződéstípusból létrehozott egyedi sztring lesz. (Más szóval a szerződés neve csak azoknak az exportálásoknak felel meg, amelyek neve szintén a típusból IMyAddinszármazik.)
Az alábbi ábrán az előző importálásnak megfelelő exportálás látható.
<Export(GetType(IMyAddin))>
Public Class MyLogger
Implements IMyAddin
End Class
[Export(typeof(IMyAddin))]
public class MyLogger : IMyAddin { }
Ebben az exportálásban a szerződés típusa IMyAddin, mert az Export attribútum paramétereként van megadva. Az exportált típusnak meg kell egyeznie a szerződés típusával, a szerződés típusából kell származnia, vagy a szerződéstípust kell implementálnia, ha az interfész. Ebben az exportálásban a tényleges típus MyLogger implementálja az interfészt IMyAddin. A szerződés neve a szerződés típusából származik, ami azt jelenti, hogy ez az exportálás megegyezik az előző importálással.
Megjegyzés:
Az exportokat és importokat általában nyilvános osztályokon vagy tagokon kell deklarálni. Az egyéb deklarációk támogatottak, de a privát, védett vagy belső tag exportálása vagy importálása megszakítja az elkülönítési modellt a rész esetében, ezért nem ajánlott.
A szerződés típusának pontosan meg kell egyeznie ahhoz, hogy az exportálás és az importálás egyezésnek minősüljön. Fontolja meg az alábbi exportálást.
<Export()> 'WILL NOT match the previous import!
Public Class MyLogger
Implements IMyAddin
End Class
[Export] //WILL NOT match the previous import!
public class MyLogger : IMyAddin { }
Ebben az exportálásban a szerződés típusa MyLogger helyett IMyAddin. Annak ellenére, hogy a MyLogger implementálja a IMyAddin-et, és ezért átalakítható egy IMyAddin objektummá, ez az export nem fog megfelelni az előző importnak, mert a szerződéstípusok nem azonosak.
Általában nem szükséges megadni a szerződés nevét, és a legtöbb szerződést a szerződés típusa és metaadatai alapján kell meghatározni. Bizonyos körülmények között azonban fontos közvetlenül megadni a szerződés nevét. A leggyakoribb eset az, ha egy osztály több olyan értéket exportál, amelyek közös típussal, például primitívekkel osztoznak. A szerződés neve megadható az Import vagy Export attribútum első paramétereként. Az alábbi kód egy importálást és egy exportálást jelenít meg a megadott szerződésnévvel MajorRevision.
Public Class MyExportClass
'This one will match
<Export("MajorRevision")>
Public ReadOnly Property MajorRevision As Integer
Get
Return 4
End Get
End Property
<Export("MinorRevision")>
Public ReadOnly Property MinorRevision As Integer
Get
Return 16
End Get
End Property
End Class
public class MyClass
{
[Import("MajorRevision")]
public int MajorRevision { get; set; }
}
public class MyExportClass
{
[Export("MajorRevision")] //This one will match.
public int MajorRevision = 4;
[Export("MinorRevision")]
public int MinorRevision = 16;
}
Ha a szerződés típusa nincs megadva, akkor is az importálás vagy exportálás típusából lesz kikövetkeztetve. Ha azonban a szerződés neve explicit módon van megadva, a szerződés típusának pontosan meg kell egyeznie ahhoz, hogy az importálás és exportálás egyezésnek minősüljön. Ha például a MajorRevision mező egy sztring, akkor a kikövetkeztetett szerződéstípusok nem egyeznek, és az exportálás nem egyezik az importálással, annak ellenére, hogy ugyanaz a szerződésnév van megadva.
Metódus importálása és exportálása
Az Export attribútum ugyanúgy díszíthet egy metódust, mint egy osztály, tulajdonság vagy függvény. A metódusexportálásnak meg kell adnia egy szerződéstípust vagy szerződésnevet, mivel a típus nem következtethető ki. A megadott típus lehet egyéni delegált vagy általános típus, például Func. A következő osztály exportál egy metódust DoSomething.
Public Class MyAddin
'Explicitly specifying a generic type
<Export(GetType(Func(Of Integer, String)))>
Public Function DoSomething(ByVal TheParam As Integer) As String
Return Nothing 'Function body goes here
End Function
End Class
public class MyAddin
{
//Explicitly specifying a generic type.
[Export(typeof(Func<int, string>))]
public string DoSomething(int TheParam);
}
Ebben az osztályban a DoSomething metódus egyetlen int paramétert használ, és visszaad egy string. Az exportálásnak megfelelően az importáló résznek megfelelő tagot kell deklarálnia. A következő osztály importálja a metódust DoSomething .
Public Class MyClass1
'Contract name must match!
<Import()>
Public Property MajorRevision As Func(Of Integer, String)
End Class
public class MyClass
{
[Import] //Contract name must match!
public Func<int, string> DoSomething { get; set; }
}
További információ az Func<T, T> objektum használatáról: Func<T,TResult>.
Importtípusok
A MEF számos importálási típust támogat, például dinamikus, lusta, előfeltétel és választható.
Dinamikus importálás
Bizonyos esetekben előfordulhat, hogy az importálási osztálynak meg kell egyeznie egy adott szerződésnévvel rendelkező bármilyen típusú exportálással. Ebben a forgatókönyvben az osztály dinamikus importálást deklarálhat. Az alábbi importálás megegyezik a "TheString" szerződésnévvel rendelkező exportálásokkal.
Public Class MyClass1
<Import("TheString")>
Public Property MyAddin
End Class
public class MyClass
{
[Import("TheString")]
public dynamic MyAddin { get; set; }
}
Ha a szerződés típusa a dynamic kulcsszóból származik, az minden szerződéstípusnak megfelel. Ebben az esetben az importálásnak mindig meg kell adnia a szerződésnevet, amellyel egyezni kíván. (Ha nincs megadva szerződésnév, az importálás nem fog exportnak minősülni.) Az alábbi exportálások mindegyike megfelel az előző importálásnak.
<Export("TheString", GetType(IMyAddin))>
Public Class MyLogger
Implements IMyAddin
End Class
<Export("TheString")>
Public Class MyToolbar
End Class
[Export("TheString", typeof(IMyAddin))]
public class MyLogger : IMyAddin { }
[Export("TheString")]
public class MyToolbar { }
Nyilvánvaló, hogy az importáló osztálynak fel kell készülnie egy tetszőleges típusú objektum kezelésére.
Lusta importálás
Bizonyos esetekben előfordulhat, hogy az importálási osztály közvetett hivatkozást igényel az importált objektumra, hogy az objektum ne legyen azonnal példányosítva. Ebben az esetben az osztály egy szerződéstípus használatával deklarálhat egy lusta importálástLazy<T>. Az alábbi importálási tulajdonság lusta importálást deklarál.
Public Class MyClass1
<Import()>
Public Property MyAddin As Lazy(Of IMyAddin)
End Class
public class MyClass
{
[Import]
public Lazy<IMyAddin> MyAddin { get; set; }
}
Az összeállítási motor szempontjából a Lazy<T> szerződéstípus megegyezik a T szerződéstípusával. Ezért az előző importálás megfelel az alábbi exportálásnak.
<Export(GetType(IMyAddin))>
Public Class MyLogger
Implements IMyAddin
End Class
[Export(typeof(IMyAddin))]
public class MyLogger : IMyAddin { }
A szerződés neve és szerződéstípusa megadható egy Import lusta importálás attribútumában, az "Alapszintű importálás és exportálás" szakaszban leírtak szerint.
Előfeltételek importálása
Az exportált MEF-alkatrészeket általában a kompozíciós motor hozza létre egy közvetlen kérésre vagy egy egyeztetett importálás kitöltésének szükségességére válaszul. Alapértelmezés szerint egy alkatrész létrehozásakor a kompozíciós motor a paraméter nélküli konstruktort használja. Ha azt szeretné, hogy a motor más konstruktort használjon, megjelölheti az ImportingConstructor attribútummal.
Minden egyes rész csak egy konstruktorsal rendelkezhet, amelyet a kompozíciós motor használ. Ha nem ad meg paraméter nélküli konstruktort és nem ImportingConstructor ad meg attribútumot, vagy egynél ImportingConstructor több attribútumot ad meg, az hibát fog eredményezni.
Az attribútummal megjelölt konstruktor paramétereinek kitöltéséhez az ImportingConstructor összes paraméter automatikusan importálásként lesz deklarálva. Ez egy kényelmes módja annak, hogy deklarálja azokat az importokat, amelyeket a rész inicializálása során használnak. Az alábbi osztály egy importálás deklarálására használja ImportingConstructor .
Public Class MyClass1
Private _theAddin As IMyAddin
'Parameterless constructor will NOT be used
'because the ImportingConstructor
'attribute is present.
Public Sub New()
End Sub
'This constructor will be used.
'An import with contract type IMyAddin
'is declared automatically.
<ImportingConstructor()>
Public Sub New(ByVal MyAddin As IMyAddin)
_theAddin = MyAddin
End Sub
End Class
public class MyClass
{
private IMyAddin _theAddin;
//Parameterless constructor will NOT be
//used because the ImportingConstructor
//attribute is present.
public MyClass() { }
//This constructor will be used.
//An import with contract type IMyAddin is
//declared automatically.
[ImportingConstructor]
public MyClass(IMyAddin MyAddin)
{
_theAddin = MyAddin;
}
}
Alapértelmezés szerint az attribútum az ImportingConstructor összes paraméterimportáláshoz a kikövetkeztetett szerződéstípusokat és szerződésneveket használja. Ezt felül lehet bírálni úgy, hogy a paramétereket Import attribútumokkal látja el, amelyek kifejezetten meghatározhatják a szerződés típusát és a szerződés nevét. Az alábbi kód egy konstruktort mutat be, amely ezt a szintaxist használja egy származtatott osztály importálásához szülőosztály helyett.
<ImportingConstructor()>
Public Sub New(<Import(GetType(IMySubAddin))> ByVal MyAddin As IMyAddin)
End Sub
[ImportingConstructor]
public MyClass([Import(typeof(IMySubAddin))]IMyAddin MyAddin)
{
_theAddin = MyAddin;
}
Különösen óvatosnak kell lennie a gyűjtési paraméterekkel. Ha például egy paraméterrel ImportingConstructor rendelkező konstruktort ad meg IEnumerable<int>, az importálás egyetlen IEnumerable<int> típusú exporttal egyezik meg, nem pedig egy int típusú exportokból álló készlettel. A int típusú exportkészlet egyeztetéséhez a paramétert el kell látni az ImportMany attribútummal.
Az attribútum által ImportingConstructor importálásként deklarált paraméterek is előfeltétel-importálásként vannak megjelölve. A MEF általában lehetővé teszi, hogy az export és az import ciklust alkotjon. Egy ciklus például az, ahol az A objektum importálja a B objektumot, amely viszont importálja az A objektumot. Normál körülmények között a ciklus nem probléma, és a kompozíciós tároló általában mindkét objektumot felépíti.
Ha egy alkatrész konstruktorának importált értékre van szüksége, az objektum nem vehet részt egy ciklusban. Ha az A objektum megköveteli, hogy a B objektum létre legyen hozva, mielőtt megépülne, és a B objektum importálja az A objektumot, akkor a ciklus nem oldható fel, és összeállítási hiba lép fel. A konstruktorparamétereken deklarált importok tehát előfeltételként szolgáló importálások, amelyeket mindenképpen ki kell tölteni, mielőtt az azt igénylő objektumból származó exportálások bármelyike felhasználható lenne.
Opcionális importálás
Az Import attribútum egy követelményt határoz meg a rész működéséhez. Ha egy importálás nem hajtható végre, az adott rész összetétele sikertelen lesz, és a rész nem lesz elérhető.
A tulajdonság használatával megadhatja, hogy az importálás AllowDefault. Ebben az esetben az összeállítás akkor is sikeres lesz, ha az importálás nem egyezik a rendelkezésre álló exportálásokkal, és az importálási tulajdonság a tulajdonságtípus alapértelmezett értékére lesz állítva (null objektumtulajdonságok esetén, false logikai értékek esetén, numerikus tulajdonságok esetében nulla). Az alábbi osztály nem kötelező importálást használ.
Public Class MyClass1
<Import(AllowDefault:=True)>
Public Property thePlugin As Plugin
'If no matching export is available,
'thePlugin will be set to null.
End Class
public class MyClass
{
[Import(AllowDefault = true)]
public Plugin thePlugin { get; set; }
//If no matching export is available,
//thePlugin will be set to null.
}
Több objektum importálása
Az Import attribútum csak akkor lesz sikeresen összeállítva, ha egy és csak egy exportálásnak felel meg. Más esetekben összeállítási hiba lép fel. Ha egynél több, azonos szerződésnek megfelelő exportálást szeretne importálni, használja az ImportMany attribútumot. Az ezzel az attribútummal megjelölt importálások mindig nem kötelezőek. Az összetétel például nem fog zátonyra futni, ha nincsenek hozzáillő exportok. Az alábbi osztály tetszőleges számú típusú exportálást IMyAddinimportál.
Public Class MyClass1
<ImportMany()>
Public Property MyAddin As IEnumerable(Of IMyAddin)
End Class
public class MyClass
{
[ImportMany]
public IEnumerable<IMyAddin> MyAddin { get; set; }
}
Az importált tömb a szokásos IEnumerable<T> szintaxissal és metódusokkal érhető el. Ehelyett egy szokásos tömböt (IMyAddin[]) is használhat.
Ez a minta nagyon fontos lehet, amikor a Lazy<T> szintaxissal együtt használja. A ImportMany, IEnumerable<T> és Lazy<T> használatával például tetszőleges számú objektumra importálhat közvetett hivatkozásokat, és csak a szükségeseket példányosíthatja. Az alábbi osztály ezt a mintát mutatja.
Public Class MyClass1
<ImportMany()>
Public Property MyAddin As IEnumerable(Of Lazy(Of IMyAddin))
End Class
public class MyClass
{
[ImportMany]
public IEnumerable<Lazy<IMyAddin>> MyAddin { get; set; }
}
A felderítés elkerülése
Bizonyos esetekben előfordulhat, hogy meg szeretné akadályozni, hogy egy rész a katalógus részeként legyen felderítve. A rész lehet például egy alaposztály, amelyből örökölni szeretné, de nem használja. Ennek kétféle módja van. Először is használhatja a kulcsszót abstract a részosztályban. Az absztrakt osztályok soha nem biztosítanak exportokat, bár örökölhető exportokat biztosíthatnak az azokból származó osztályoknak.
Ha az osztályt nem lehet absztrakttá tenni, elláthatja a PartNotDiscoverable attribútummal. Az ezzel az attribútummal díszített rész nem fog szerepelni a katalógusokban. Az alábbi példa ezeket a mintákat mutatja be. A katalógus fel fogja deríteni DataOne. Mivel DataTwo absztrakcióról van szó, a rendszer nem fogja felderíteni. Mivel a DataThree attribútumot a PartNotDiscoverable használta, ez nem lesz felfedezhető.
<Export()>
Public Class DataOne
'This part will be discovered
'as normal by the catalog.
End Class
<Export()>
Public MustInherit Class DataTwo
'This part will not be discovered
'by the catalog.
End Class
<PartNotDiscoverable()>
<Export()>
Public Class DataThree
'This part will also not be discovered
'by the catalog.
End Class
[Export]
public class DataOne
{
//This part will be discovered
//as normal by the catalog.
}
[Export]
public abstract class DataTwo
{
//This part will not be discovered
//by the catalog.
}
[PartNotDiscoverable]
[Export]
public class DataThree
{
//This part will also not be discovered
//by the catalog.
}
Metaadatok és metaadat nézetek
Az exportálások további információkat nyújthatnak magukról, más néven metaadatokról. A metaadatok az exportált objektum tulajdonságainak átvitelére használhatók az importáló részre. Az importáló rész ezeket az adatokat arra használhatja, hogy eldöntse, melyik exportálást használja, vagy adatokat gyűjtsön az exportálásról anélkül, hogy létre kellene hoznia azokat. Ezért az importálásnak lustanak kell lennie a metaadatok használatához.
A metaadatok használatához általában egy metaadatnézetként ismert felületet deklarál, amely deklarálja, hogy mely metaadatok lesznek elérhetők. A metaadat-nézet felületének csak tulajdonságokkal kell rendelkeznie, és ezeknek a tulajdonságoknak get hozzáférési metódusokkal kell rendelkezniük. Az alábbi felület egy példa metaadat-nézet.
Public Interface IPluginMetadata
ReadOnly Property Name As String
<DefaultValue(1)>
ReadOnly Property Version As Integer
End Interface
public interface IPluginMetadata
{
string Name { get; }
[DefaultValue(1)]
int Version { get; }
}
Általános gyűjteményt IDictionary<string, object> használhat metaadat nézetként, de ez elveszíti a típusellenőrzés előnyeit, ezért el kell kerülni.
Általában a metaadat nézetben megnevezett összes tulajdonság szükséges, és az olyan exportok, amelyek nem biztosítják ezeket, nem fognak egyezésnek számítani. Az DefaultValue attribútum azt határozza meg, hogy egy tulajdonság nem kötelező. Ha a tulajdonság nem szerepel a fájlban, akkor a rendszer a paraméterként megadott alapértelmezett értéket rendeli DefaultValuehozzá. Az alábbiak két különböző osztályt jelölnek metaadatokkal. Mindkét osztály megfelelne az előző metaadat-nézetnek.
<Export(GetType(IPlugin))>
<ExportMetadata("Name", "Logger")>
<ExportMetadata("Version", 4)>
Public Class MyLogger
Implements IPlugin
End Class
'Version is not required because of the DefaultValue
<Export(GetType(IPlugin))>
<ExportMetadata("Name", "Disk Writer")>
Public Class DWriter
Implements IPlugin
End Class
[Export(typeof(IPlugin)),
ExportMetadata("Name", "Logger"),
ExportMetadata("Version", 4)]
public class Logger : IPlugin
{
}
[Export(typeof(IPlugin)),
ExportMetadata("Name", "Disk Writer")]
//Version is not required because of the DefaultValue
public class DWriter : IPlugin
{
}
A metaadatok a Export attribútum után a ExportMetadata attribútum használatával lesznek kifejezve. Minden metaadat egy név-érték párból áll. A metaadatok névrészének meg kell egyeznie a metaadatok nézetben a megfelelő tulajdonság nevével, és az érték hozzá lesz rendelve ehhez a tulajdonsághoz.
Az importáló határozza meg, hogy milyen metaadat-nézet lesz használatban, ha van ilyen. Az olyan importokat, amelyek metaadatokat tartalmaznak, lusta importálásként kell deklarálni, ahol a metaadatokat tartalmazó interfész a második típusparaméter a Lazy<T,T>-hoz. Az alábbi osztály metaadatokkal importálja az előző részt.
Public Class Addin
<Import()>
Public Property plugin As Lazy(Of IPlugin, IPluginMetadata)
End Class
public class Addin
{
[Import]
public Lazy<IPlugin, IPluginMetadata> plugin;
}
Sok esetben érdemes a metaadatokat az ImportMany attribútummal kombinálni, hogy elemezni lehessen az elérhető importálásokat, és csak egyet válasszon ki és példányosíthasson, vagy szűrjön egy gyűjteményt egy adott feltételnek megfelelően. Az alábbi osztály csakis IPlugin objektumokat példányosít, amelyeknek Name értéke "Logger".
Public Class User
<ImportMany()>
Public Property plugins As IEnumerable(Of Lazy(Of IPlugin, IPluginMetadata))
Public Function InstantiateLogger() As IPlugin
Dim logger As IPlugin
logger = Nothing
For Each Plugin As Lazy(Of IPlugin, IPluginMetadata) In plugins
If Plugin.Metadata.Name = "Logger" Then
logger = Plugin.Value
End If
Next
Return logger
End Function
End Class
public class User
{
[ImportMany]
public IEnumerable<Lazy<IPlugin, IPluginMetadata>> plugins;
public IPlugin InstantiateLogger()
{
IPlugin logger = null;
foreach (Lazy<IPlugin, IPluginMetadata> plugin in plugins)
{
if (plugin.Metadata.Name == "Logger")
logger = plugin.Value;
}
return logger;
}
}
Öröklés importálása és exportálása
Ha egy osztály egy résztől öröklődik, az az osztály is része lehet. Az importok mindig alosztályok által átvételre kerülnek. Ezért egy rész alosztálya mindig része marad, ugyanazokkal az importokkal, mint a szülőosztálya.
A Export attribútummal deklarált exportok nem öröklődnek az alosztályokban. Egy rész azonban exportálhatja magát az InheritedExport attribútum használatával. A rész alosztályai megöröklik és biztosítják ugyanazt az exportálást, beleértve a szerződésnevet és a szerződéstípust.
Export Egy attribútumtól InheritedExport eltérően csak az osztály szintjén alkalmazható, tagszinten nem. Ezért a tagszintű exportálások soha nem örökölhetők.
Az alábbi négy osztály az importálási és exportálási öröklés alapelveit mutatja be.
NumTwo örököl NumOne-ből, így NumTwo be fogja importálni IMyData-t. A szokásos exportálások nem öröklődnek, így NumTwo semmit sem exportálnak.
NumFour örököl NumThree-től. Mivel NumThree felhasználta InheritedExport, NumFour egy szerződés típusú exporttal rendelkezik NumThree. A tagszintű exportálások soha nem öröklődnek, ezért IMyData nem exportálódnak.
<Export()>
Public Class NumOne
<Import()>
Public Property MyData As IMyData
End Class
Public Class NumTwo
Inherits NumOne
'Imports are always inherited, so NumTwo will
'Import IMyData
'Ordinary exports are not inherited, so
'NumTwo will NOT export anything. As a result it
'will not be discovered by the catalog!
End Class
<InheritedExport()>
Public Class NumThree
<Export()>
Public Property MyData As IMyData
'This part provides two exports, one of
'contract type NumThree, and one of
'contract type IMyData.
End Class
Public Class NumFour
Inherits NumThree
'Because NumThree used InheritedExport,
'this part has one export with contract
'type NumThree.
'Member-level exports are never inherited,
'so IMyData is not exported.
End Class
[Export]
public class NumOne
{
[Import]
public IMyData MyData { get; set; }
}
public class NumTwo : NumOne
{
//Imports are always inherited, so NumTwo will
//import IMyData.
//Ordinary exports are not inherited, so
//NumTwo will NOT export anything. As a result it
//will not be discovered by the catalog!
}
[InheritedExport]
public class NumThree
{
[Export]
Public IMyData MyData { get; set; }
//This part provides two exports, one of
//contract type NumThree, and one of
//contract type IMyData.
}
public class NumFour : NumThree
{
//Because NumThree used InheritedExport,
//this part has one export with contract
//type NumThree.
//Member-level exports are never inherited,
//so IMyData is not exported.
}
Ha egy attribútumhoz InheritedExport metaadatok vannak társítva, a metaadatok is öröklődnek. (További információkért lásd a korábbi "Metaadatok és metaadatok nézetei" szakaszt.) Az örökölt metaadatokat az alosztály nem módosíthatja. Ha azonban az attribútumot ugyanazzal a InheritedExport szerződésnévvel és szerződéstípussal deklarálja, de új metaadatokkal, az alosztály az örökölt metaadatokat új metaadatokra cserélheti. Az alábbi osztály ezt az elvet mutatja be. A MegaLogger rész öröklődik a(z) Logger részről, és tartalmazza a InheritedExport attribútumot. Mivel MegaLogger újra deklarálja az Állapot nevű új metaadatokat, nem örökli a név és a verzió metaadatait.Logger
<InheritedExport(GetType(IPlugin))>
<ExportMetadata("Name", "Logger")>
<ExportMetadata("Version", 4)>
Public Class Logger
Implements IPlugin
'Exports with contract type IPlugin
'and metadata "Name" and "Version".
End Class
Public Class SuperLogger
Inherits Logger
'Exports with contract type IPlugin and
'metadata "Name" and "Version", exactly the same
'as the Logger class.
End Class
<InheritedExport(GetType(IPlugin))>
<ExportMetadata("Status", "Green")>
Public Class MegaLogger
Inherits Logger
'Exports with contract type IPlugin and
'metadata "Status" only. Re-declaring
'the attribute replaces all metadata.
End Class
[InheritedExport(typeof(IPlugin)),
ExportMetadata("Name", "Logger"),
ExportMetadata("Version", 4)]
public class Logger : IPlugin
{
//Exports with contract type IPlugin and
//metadata "Name" and "Version".
}
public class SuperLogger : Logger
{
//Exports with contract type IPlugin and
//metadata "Name" and "Version", exactly the same
//as the Logger class.
}
[InheritedExport(typeof(IPlugin)),
ExportMetadata("Status", "Green")]
public class MegaLogger : Logger {
//Exports with contract type IPlugin and
//metadata "Status" only. Re-declaring
//the attribute replaces all metadata.
}
Ha újra deklarálja az attribútumot a InheritedExport metaadatok felülbírálása érdekében, győződjön meg arról, hogy a szerződéstípusok megegyeznek. (Az előző példában IPlugin a szerződés típusa.) Ha eltérnek, a felülírás helyett a második attribútum létrehoz egy második, független exportálást a részből. Általában ez azt jelenti, hogy egy attribútum felülírásakor explicit módon meg kell adnia a szerződés típusát, ahogy az előző példában is látható.
Mivel a felületeket nem lehet közvetlenül példányosítani, általában nem lehet őket Export vagy Import attribútumokkal ellátni. Az illesztőfelületek azonban egy attribútummal InheritedExport díszíthetők a felület szintjén, és az exportálást a kapcsolódó metaadatokkal együtt minden implementáló osztály örökli. Maga a felület azonban nem lesz elérhető alkatrészként.
Egyéni exportálási attribútumok
Az alapvető exportálási attribútumok és ExportInheritedExporta metaadatok attribútumtulajdonságokként való belefoglalására bővíthetők. Ez a technika hasznos lehet hasonló metaadatok több részre történő alkalmazásához, vagy a metaadat-attribútumok öröklési fájának létrehozásához.
Egy egyéni attribútum megadhatja a szerződés típusát, a szerződés nevét vagy bármely más metaadatot. Egyéni attribútum definiálásához egy osztály, amely az ExportAttribute (vagy InheritedExportAttribute) osztályból öröklődik, dekorálni kell az MetadataAttribute attribútummal. Az alábbi osztály egy egyéni attribútumot határoz meg.
<MetadataAttribute()>
<AttributeUsage(AttributeTargets.Class, AllowMultiple:=false)>
Public Class MyAttribute
Inherits ExportAttribute
Public Property MyMetadata As String
Public Sub New(ByVal myMetadata As String)
MyBase.New(GetType(IMyAddin))
myMetadata = myMetadata
End Sub
End Class
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
public class MyAttribute : ExportAttribute
{
public MyAttribute(string myMetadata)
: base(typeof(IMyAddin))
{
MyMetadata = myMetadata;
}
public string MyMetadata { get; private set; }
}
Ez az osztály egy MyAttribute nevű egyéni attribútumot határoz meg szerződéstípussal IMyAddin és néhány MyMetadata nevű metaadattal. Az attribútummal MetadataAttribute megjelölt osztály összes tulajdonsága az egyéni attribútumban definiált metaadatoknak minősül. Az alábbi két deklaráció egyenértékű.
<Export(GetType(IMyAddin))>
<ExportMetadata("MyMetadata", "theData")>
Public Property myAddin As MyAddin
<MyAttribute("theData")>
Public Property myAddin As MyAddin
[Export(typeof(IMyAddin)),
ExportMetadata("MyMetadata", "theData")]
public MyAddin myAddin { get; set; }
[MyAttribute("theData")]
public MyAddin myAddin { get; set; }
Az első deklarációban a szerződés típusa és a metaadatok explicit módon vannak definiálva. A második deklarációban a szerződés típusa és metaadatai implicitek a testreszabott attribútumban. Különösen azokban az esetekben, amikor nagy mennyiségű azonos metaadatot kell alkalmazni számos részre (például szerzői vagy szerzői jogi információkra), az egyéni attribútumok használata sok időt és duplikációt takaríthat meg. Emellett egyéni attribútumok öröklőfái is létrehozhatók, hogy lehetővé tegyék a variációkat.
Ha egyéni attribútumban nem kötelező metaadatokat szeretne létrehozni, használhatja az DefaultValue attribútumot. Ha ezt az attribútumot egy egyéni attribútumosztály egyik tulajdonságára alkalmazza, az azt határozza meg, hogy a dekorált tulajdonság nem kötelező, és nem kell az exportőrnek megadnia. Ha a tulajdonság értéke nincs megadva, akkor a tulajdonságtípus alapértelmezett értéke lesz hozzárendelve (általában nullfalse0 vagy 0).
Létrehozási szabályzatok
Amikor egy rész importálást és összeállítást határoz meg, a kompozíciós tároló megkísérli megtalálni az egyező exportálást. Ha az importálás sikeresen összepárosítható egy exportálással, akkor az importáló tagot az exportált objektum egy példányára állítják. Az exportálási rész létrehozási szabályzata szabályozza, hogy honnan származik ez a példány.
A két lehetséges létrehozási házirend megosztott és nem megosztott. A megosztott létrehozási elvvel rendelkező komponens meg lesz osztva a környezet minden importálása között az adott szerződéssel rendelkező komponens esetében. Ha a kompozíciós motor talál egyezést, és importáló tulajdonságot kell beállítania, csak akkor példányosít egy új példányt a részből, ha még nem létezik; ellenkező esetben a meglévő példányt adja meg. Ez azt jelenti, hogy sok objektum hivatkozhat ugyanarra a részre. Az ilyen részek nem támaszkodhatnak olyan belső állapotra, amely sok helyről módosítható. Ez a szabályzat a statikus részekhez, a szolgáltatásokat biztosító alkatrészekhez és a sok memóriát vagy más erőforrást használó részekhez megfelelő.
A nem megosztott létrehozási szabályzattal rendelkező rész minden alkalommal létrejön, amikor egyező importálást talál az egyik exportálásához. Ezért a tárolóban lévő minden olyan importáláshoz, amely megfelel a rész exportált szerződéseinek, egy új példány lesz példányosítva. A másolatok belső állapota nem lesz megosztva. Ez a szabályzat olyan részekhez megfelelő, ahol az egyes importálások saját belső állapotot igényelnek.
Az import és az export megadja egy rész létrehozási házirendjét az értékek Shared, NonShared vagy Any közül. Az alapértelmezett érték az Any import és az export esetében is. Olyan export, amely Shared vagy NonShared-et meghatároz, csak olyan importtal fog egyezni, amely ugyanazt vagy Any-t meghatároz. Hasonlóképpen, egy importálás, amely megadja Shared vagy NonShared csak egyezik egy olyan exportálással, amely ugyanazt adja meg, vagy amely megadja Any. Az inkompatibilis létrehozási házirendekkel rendelkező importálások és exportálások nem egyeznek, ugyanúgy, mint az olyan importálás és exportálás, amelynek szerződésneve vagy szerződéstípusa nem egyezik. Ha mind az importálás, mind az exportálás meg van adva Any, vagy nem adnak meg létrehozási házirendet, és ez alapértelmezés szerint Any lesz, akkor a létrehozási házirend alapértelmezés szerint megosztott lesz.
Az alábbi példa a létrehozási szabályzatokat meghatározó importálást és exportálást mutatja be.
PartOne nem ad meg létrehozási szabályzatot, ezért az alapértelmezett érték a következő Any.
PartTwo nem ad meg létrehozási szabályzatot, ezért az alapértelmezett érték a következő Any. Mivel az importálás és az exportálás alapértelmezésként Any, meg PartOne lesz osztva.
PartThree egy létrehozási szabályzatot Shared határoz meg, így PartTwoPartThree ugyanazzal a másolattal PartOnefog osztozni.
PartFour egy létrehozási szabályzatot NonShared határoz meg, így PartFour nem lesz megosztva az PartFive-ben.
PartSix egy létrehozási szabályzatot NonShared határoz meg.
PartFive és PartSix mindegyik külön másolatot kap a PartFour.
PartSeven egy létrehozási szabályzatot Shared határoz meg. Mivel nincs exportált PartFour azzal a létrehozási házirenddel, ami Shared, az PartSeven importálás nem fog semmihez illeszkedni, és nem lesz kitöltve.
<Export()>
Public Class PartOne
'The default creation policy for an export is Any.
End Class
Public Class PartTwo
<Import()>
Public Property partOne As PartOne
'The default creation policy for an import is Any.
'If both policies are Any, the part will be shared.
End Class
Public Class PartThree
<Import(RequiredCreationPolicy:=CreationPolicy.Shared)>
Public Property partOne As PartOne
'The Shared creation policy is explicitly specified.
'PartTwo and PartThree will receive references to the
'SAME copy of PartOne.
End Class
<Export()>
<PartCreationPolicy(CreationPolicy.NonShared)>
Public Class PartFour
'The NonShared creation policy is explicitly specified.
End Class
Public Class PartFive
<Import()>
Public Property partFour As PartFour
'The default creation policy for an import is Any.
'Since the export's creation policy was explicitly
'defined, the creation policy for this property will
'be non-shared.
End Class
Public Class PartSix
<Import(RequiredCreationPolicy:=CreationPolicy.NonShared)>
Public Property partFour As PartFour
'Both import and export specify matching creation
'policies. PartFive and PartSix will each receive
'SEPARATE copies of PartFour, each with its own
'internal state.
End Class
Public Class PartSeven
<Import(RequiredCreationPolicy:=CreationPolicy.Shared)>
Public Property partFour As PartFour
'A creation policy mismatch. Because there is no
'exported PartFour with a creation policy of Shared,
'this import does not match anything and will not be
'filled.
End Class
[Export]
public class PartOne
{
//The default creation policy for an export is Any.
}
public class PartTwo
{
[Import]
public PartOne partOne { get; set; }
//The default creation policy for an import is Any.
//If both policies are Any, the part will be shared.
}
public class PartThree
{
[Import(RequiredCreationPolicy = CreationPolicy.Shared)]
public PartOne partOne { get; set; }
//The Shared creation policy is explicitly specified.
//PartTwo and PartThree will receive references to the
//SAME copy of PartOne.
}
[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class PartFour
{
//The NonShared creation policy is explicitly specified.
}
public class PartFive
{
[Import]
public PartFour partFour { get; set; }
//The default creation policy for an import is Any.
//Since the export's creation policy was explicitly
//defined, the creation policy for this property will
//be non-shared.
}
public class PartSix
{
[Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
public PartFour partFour { get; set; }
//Both import and export specify matching creation
//policies. PartFive and PartSix will each receive
//SEPARATE copies of PartFour, each with its own
//internal state.
}
public class PartSeven
{
[Import(RequiredCreationPolicy = CreationPolicy.Shared)]
public PartFour partFour { get; set; }
//A creation policy mismatch. Because there is no
//exported PartFour with a creation policy of Shared,
//this import does not match anything and will not be
//filled.
}
Életciklus és ártalmatlanítás
Mivel az alkatrészek a kompozíciós tárolóban vannak tárolva, életciklusuk összetettebb lehet, mint a szokásos objektumok. Az alkatrészek két fontos, életciklussal kapcsolatos interfészt valósíthatnak meg: IDisposable és IPartImportsSatisfiedNotification.
Azokat a részeket, amelyeknél le kell állítani a munkát, vagy erőforrásokat kell felszabadítani, IDisposable kell implementálniuk, ahogy az .NET objektumok esetében szokás. Mivel azonban a tároló alkatrészekre mutató hivatkozásokat hoz létre és tart fenn, csak az a tároló hívja meg a Dispose metódust, amely egy alkatrészt birtokol. Maga a tároló implementálja IDisposable, és a benne lévő Dispose tisztítás részeként meghívja a Dispose a tulajdonában lévő összes alkatrészre. Ezért mindig el kell helyeznie a kompozíciós tárolót, amikor már nincs rá szükség, és a tulajdonában lévő részekre már nincs szükség.
A hosszú élettartamú összeállítási tárolók esetében a nem megosztott létrehozási szabályzattal rendelkező részek memóriahasználata problémát okozhat. Ezek a nem megosztott részek többször is létrehozhatók, és nem lesznek megsemmisítve, amíg magát a tárolót el nem dobja. Ennek kezeléséhez a tároló biztosítja a metódust ReleaseExport . Ha ezt a metódust nem megosztott exportra hívja meg, az eltávolítja az exportot a kompozíciós tárolóból, és megsemmisíti azt. Azokat a részeket, amelyeket csak az eltávolított export használ, és így tovább a hierarchiában lefelé, szintén eltávolítják és ártalmatlanítják. Ily módon az erőforrások a kompozíciós tároló eltávolítása nélkül is visszanyerhetők.
IPartImportsSatisfiedNotification egy metódust tartalmaz, amit OnImportsSatisfied-nek neveznek. Ezt a módszert az összeállítási tároló hívja meg minden olyan résznél, amely az összeállítás befejezésekor implementálja az interfészt, és a rész importálása használatra kész. Az alkatrészeket az összeállítási motor hozza létre, hogy kitöltse a többi alkatrész behozatalát. Egy alkatrész importálásának beállítása előtt nem hajthat végre olyan inicializálást, amely az importált értékekre támaszkodik vagy módosítja azokat az alkatrész-konstruktorban, kivéve, ha ezeket az értékeket előfeltételként adták meg az ImportingConstructor attribútum használatával. Általában ez az előnyben részesített módszer, de bizonyos esetekben előfordulhat, hogy a konstruktorinjektálás nem érhető el. Ezekben az esetekben az inicializálás elvégezhető OnImportsSatisfied, és a résznek IPartImportsSatisfiedNotification-t kell implementálnia.