Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
In het Managed Extensibility Framework (MEF) is een programmeermodel een bepaalde methode voor het definiëren van de set conceptuele objecten waarop MEF werkt. Deze conceptuele objecten omvatten onderdelen, import en export. MEF gebruikt deze objecten, maar geeft niet op hoe ze moeten worden weergegeven. Daarom zijn er diverse programmeermodellen mogelijk, waaronder aangepaste programmeermodellen.
Het standaardprogrammeermodel dat in MEF wordt gebruikt, is het toegeschreven programmeermodel. In de toegewezen onderdelen van het programmeermodel worden import-, export- en andere objecten gedefinieerd met kenmerken die gewone .NET klassen versieren. In dit artikel wordt uitgelegd hoe u de kenmerken van het toegewezen programmeermodel gebruikt om een MEF-toepassing te maken.
Basisbeginselen importeren en exporteren
Een export is een waarde die een onderdeel levert aan andere onderdelen in de container en een import is een vereiste die een onderdeel aan de container stelt, die moet worden vervuld met de beschikbare exports. In het toegewezen programmeermodel worden import- en exports gedeclareerd door klassen of leden met de Import en Export kenmerken te decoreren. Het Export kenmerk kan een klasse, veld, eigenschap of methode versieren, terwijl het Import kenmerk een veld, eigenschap of constructorparameter kan versieren.
Als u wilt dat een import overeenkomt met een export, moet de import en export hetzelfde contract hebben. Het contract bestaat uit een tekenreeks, de naam van het contract en het type geëxporteerde of geïmporteerde object, genaamd het contracttype. Alleen als zowel de contractnaam als het contracttype overeenkomen, wordt een export beschouwd als vervullend voor een bepaalde import.
Een van beide contractparameters kan impliciet of expliciet zijn. De volgende code toont een klasse die een basisimport declareert.
Public Class MyClass1
<Import()>
Public Property MyAddin As IMyAddin
End Class
public class MyClass
{
[Import]
public IMyAddin MyAddin { get; set; }
}
Bij deze import heeft het Import kenmerk geen contracttype of een parameter voor de contractnaam gekoppeld. Daarom worden beide afgeleid van het ingerichte pand. In dit geval is IMyAddinhet contracttype en de contractnaam is een unieke tekenreeks die is gemaakt op basis van het contracttype. (Met andere woorden, de contractnaam komt alleen overeen met exports waarvan de namen ook worden afgeleid van het type IMyAddin.)
Hieronder ziet u een export die overeenkomt met de vorige import.
<Export(GetType(IMyAddin))>
Public Class MyLogger
Implements IMyAddin
End Class
[Export(typeof(IMyAddin))]
public class MyLogger : IMyAddin { }
In deze export is IMyAddin het contracttype omdat het is opgegeven als een parameter van het Export kenmerk. Het geëxporteerde type moet hetzelfde zijn als het contracttype, uit het contracttype voortkomen, of het contracttype implementeren indien het een interface is. In deze export implementeert het werkelijke type MyLogger de interface IMyAddin. De contractnaam wordt afgeleid van het contracttype, wat betekent dat deze export overeenkomt met de vorige import.
Opmerking
Uitvoer en invoer moeten meestal worden gedeclareerd voor openbare klassen of leden. Andere declaraties worden ondersteund, maar het exporteren of importeren van een privé, beveiligd of intern element van een component breekt het isolatiemodel voor het onderdeel af en wordt daarom niet aanbevolen.
Het contracttype moet exact overeenkomen voor de export en import om als overeenkomst te worden beschouwd. Houd rekening met de volgende export.
<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 { }
In deze export is MyLogger het contracttype in plaats van IMyAddin. Hoewel MyLoggerIMyAddin implementeert en daarom naar een IMyAddin object kan worden gecast, komt deze export niet overeen met de vorige import omdat de contracttypen niet hetzelfde zijn.
Over het algemeen is het niet nodig om de naam van het contract op te geven en de meeste contracten moeten worden gedefinieerd in termen van het contracttype en de metagegevens. Onder bepaalde omstandigheden is het echter belangrijk om de naam van het contract rechtstreeks op te geven. Het meest voorkomende geval is wanneer een klasse verschillende waarden exporteert die een gemeenschappelijk type delen, zoals primitieven. De contractnaam kan worden opgegeven als de eerste parameter van het Import of Export kenmerk. De volgende code toont een import en een export met een opgegeven contractnaam van 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;
}
Als het contracttype niet is opgegeven, wordt het nog steeds afgeleid van het type import of export. Zelfs als de contractnaam expliciet is opgegeven, moet het contracttype ook exact overeenkomen zodat de import en export als een match worden beschouwd. Als het MajorRevision veld bijvoorbeeld een tekenreeks was, komen de uitgestelde contracttypen niet overeen en komt de export niet overeen met de import, ondanks dezelfde contractnaam.
Een methode importeren en exporteren
Het Export kenmerk kan ook een methode versieren, op dezelfde manier als een klasse, eigenschap of functie. Methodeexports moeten een contracttype of contractnaam opgeven, omdat het type niet kan worden afgeleid. Het opgegeven type kan een aangepaste gemachtigde of een algemeen type zijn, zoals Func. De volgende klasse exporteert een methode met de naam 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);
}
In deze klasse gebruikt de DoSomething methode één int parameter en retourneert een string. Als u deze export wilt vergelijken, moet het importonderdeel een geschikt lid declareren. De volgende klasse importeert de DoSomething methode.
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; }
}
Zie voor meer informatie over het gebruik van het Func<T, T> object Func<T,TResult>.
Typen importen
MEF ondersteunt verschillende importtypen, waaronder dynamische, luie, verplichte en optionele.
Dynamische importen
In sommige gevallen wil de importerende klasse mogelijk overeenkomen met exports van elk willekeurig type dat een bepaalde contractnaam heeft. In dit scenario kan de klasse een dynamische import declareren. De volgende import komt overeen met een export met de contractnaam TheString.
Public Class MyClass1
<Import("TheString")>
Public Property MyAddin
End Class
public class MyClass
{
[Import("TheString")]
public dynamic MyAddin { get; set; }
}
Wanneer het contracttype wordt afgeleid van het dynamic trefwoord, komt het overeen met elk contracttype. In dit geval moet bij een import altijd een contractnaam worden opgegeven die overeenkomt met de overeenkomst. (Als er geen contractnaam is opgegeven, wordt de import beschouwd als niet overeenkomend met enige exports.) De volgende twee exports komen overeen met de vorige import.
<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 { }
Uiteraard moet de importklasse voorbereid zijn om een object van een willekeurig type te verwerken.
Luie import
In sommige gevallen is voor de importklasse mogelijk een indirecte verwijzing naar het geïmporteerde object vereist, zodat het object niet onmiddellijk wordt geïnstantieerd. In dit scenario kan de klasse een luie import declareren met behulp van een contracttype Lazy<T>. De volgende importeigenschap declareert een luie import.
Public Class MyClass1
<Import()>
Public Property MyAddin As Lazy(Of IMyAddin)
End Class
public class MyClass
{
[Import]
public Lazy<IMyAddin> MyAddin { get; set; }
}
Vanuit het oogpunt van de samenstellingsmotor wordt een contracttype Lazy<T> beschouwd als identiek aan het contracttype van T. Daarom komt de vorige import overeen met de volgende export.
<Export(GetType(IMyAddin))>
Public Class MyLogger
Implements IMyAddin
End Class
[Export(typeof(IMyAddin))]
public class MyLogger : IMyAddin { }
De contractnaam en het contracttype kunnen worden opgegeven in het Import kenmerk voor een luie import, zoals eerder beschreven in de sectie Basic Import and Exports.
Importvereisten
Geëxporteerde MEF-onderdelen worden doorgaans gemaakt door de samenstellingsengine, in reactie op een directe aanvraag of de noodzaak om een overeenkomende import te vullen. Bij het maken van een onderdeel gebruikt de samenstellingsengine standaard de constructor zonder parameters. Als u de engine een andere constructor wilt laten gebruiken, kunt u deze markeren met het ImportingConstructor kenmerk.
Elk onderdeel kan slechts één constructor hebben voor gebruik door de samenstellingsengine. Als u geen constructor zonder parameter en geen ImportingConstructor kenmerk opgeeft of meer dan één ImportingConstructor kenmerk opgeeft, wordt er een fout gegenereerd.
Als u de parameters van een constructor wilt vullen die is gemarkeerd met het ImportingConstructor kenmerk, worden al deze parameters automatisch gedeclareerd als importbewerkingen. Dit is een handige manier om importbewerkingen te declareren die worden gebruikt tijdens de initialisatie van onderdelen. De volgende klasse gebruikt ImportingConstructor om een import te declareren.
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;
}
}
Standaard maakt het ImportingConstructor kenmerk gebruik van uitgestelde contracttypen en contractnamen voor alle parameterimporten. Het is mogelijk om dit te overschrijven door de parameters te decoreren met Import kenmerken, die vervolgens het contracttype en de contractnaam expliciet kunnen definiëren. De volgende code demonstreert een constructor die deze syntaxis gebruikt om een afgeleide klasse te importeren in plaats van een bovenliggende klasse.
<ImportingConstructor()>
Public Sub New(<Import(GetType(IMySubAddin))> ByVal MyAddin As IMyAddin)
End Sub
[ImportingConstructor]
public MyClass([Import(typeof(IMySubAddin))]IMyAddin MyAddin)
{
_theAddin = MyAddin;
}
In het bijzonder moet u voorzichtig zijn met verzamelingsparameters. Als u bijvoorbeeld opgeeft ImportingConstructor voor een constructor met een parameter van het type IEnumerable<int>, komt het importeren overeen met één export van het type IEnumerable<int>, in plaats van een set exports van het type int. Als u een set exports van het type intwilt vergelijken, moet u de parameter voorzien van het ImportMany kenmerk.
Parameters die zijn gedeclareerd als importbewerkingen door het ImportingConstructor kenmerk, worden ook gemarkeerd als vereiste importbewerkingen. Volgens mef kunnen export- en invoerbewerkingen normaal gesproken een cyclus vormen. Een cyclus is bijvoorbeeld waar object A object B importeert, waardoor object A op zijn beurt wordt geïmporteerd. Onder normale omstandigheden is een cyclus geen probleem en de samenstellingscontainer bouwt beide objecten normaal.
Wanneer een geïmporteerde waarde is vereist door de constructor van een onderdeel, kan dat object niet deelnemen aan een cyclus. Als object A vereist dat object B wordt samengesteld voordat het kan worden samengesteld en object B object A importeert, kan de cyclus niet worden opgelost en treedt er een samenstellingsfout op. Importen die op constructorparameters zijn gedeclareerd zijn daarom vereiste importen, die allemaal moeten worden ingevuld voordat enige exporten van het object dat ze nodig heeft gebruikt kunnen worden.
Optionele importen
Het Import kenmerk specificeert een vereiste voor het onderdeel om te functioneren. Als niet aan een import kan worden voldaan, mislukt de samenstelling van dat deel en is het onderdeel niet beschikbaar.
U kunt opgeven dat een import optioneel is met behulp van de AllowDefault eigenschap. In dit geval slaagt de samenstelling, zelfs als het importeren niet overeenkomt met beschikbare exports en de importeigenschap wordt ingesteld op de standaardwaarde voor het eigenschapstype (null voor objecteigenschappen, false voor Booleaanse waarden of nul voor numerieke eigenschappen.) In de volgende klasse wordt een optionele import gebruikt.
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.
}
Meerdere objecten importeren
Het Import kenmerk wordt alleen samengesteld wanneer het overeenkomt met één en slechts één export. In andere gevallen zal een samenstellingsfout optreden. Als u meer dan één export wilt importeren die overeenkomt met hetzelfde contract, gebruikt u het ImportMany kenmerk. Importbewerkingen die zijn gemarkeerd met dit kenmerk zijn altijd optioneel. De samenstelling mislukt bijvoorbeeld niet als er geen overeenkomende exports aanwezig zijn. De volgende klasse importeert een willekeurig aantal uitvoer van het type IMyAddin.
Public Class MyClass1
<ImportMany()>
Public Property MyAddin As IEnumerable(Of IMyAddin)
End Class
public class MyClass
{
[ImportMany]
public IEnumerable<IMyAddin> MyAddin { get; set; }
}
De geïmporteerde matrix kan worden geopend met behulp van gewone IEnumerable<T> syntaxis en methoden. Het is ook mogelijk om in plaats daarvan een gewone matrix (IMyAddin[]) te gebruiken.
Dit patroon kan erg belangrijk zijn wanneer u dit gebruikt in combinatie met de Lazy<T> syntaxis. U kunt bijvoorbeeld ImportManyIEnumerable<T>Lazy<T>indirecte verwijzingen importeren naar een willekeurig aantal objecten en alleen de objecten instantiëren die nodig zijn. In de volgende klasse ziet u dit patroon.
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; }
}
Detectie vermijden
In sommige gevallen wilt u mogelijk voorkomen dat een onderdeel wordt gedetecteerd als onderdeel van een catalogus. Het onderdeel kan bijvoorbeeld een basisklasse zijn die is bedoeld om te worden overgenomen van, maar niet wordt gebruikt. Er zijn twee manieren om dit te bereiken. Eerst kunt u het abstract trefwoord in de deelklasse gebruiken. Abstracte klassen bieden nooit exports, hoewel ze overgenomen exports kunnen bieden aan klassen die hiervan zijn afgeleid.
Als de klasse niet abstract kan worden gemaakt, kunt u deze versieren met het PartNotDiscoverable kenmerk. Een deel dat met dit kenmerk is ingericht, wordt niet opgenomen in catalogi. In het volgende voorbeeld ziet u deze patronen.
DataOne wordt gedetecteerd door de catalogus. Omdat DataTwo abstract is, zal het niet ontdekt worden. Omdat DataThree het PartNotDiscoverable kenmerk is gebruikt, wordt het niet gedetecteerd.
<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.
}
Metagegevens- en metagegevensweergaven
Exports kunnen aanvullende informatie geven over zichzelf, ook wel metagegevens genoemd. Metagegevens kunnen worden gebruikt om eigenschappen van het geëxporteerde object over te brengen naar het importonderdeel. Het importerende onderdeel kan deze gegevens gebruiken om te bepalen welke exports moeten worden gebruikt of om informatie over een export te verzamelen zonder dat u deze hoeft samen te stellen. Daarom moet een import lui zijn om metagegevens te gebruiken.
Als u metagegevens wilt gebruiken, declareert u doorgaans een interface die bekend staat als een metagegevensweergave, die aangeeft welke metagegevens beschikbaar zijn. De interface voor de metagegevensweergave mag alleen eigenschappen hebben en deze eigenschappen moeten toegangsrechten hebben get . De volgende interface is een voorbeeld van een metagegevensweergave.
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; }
}
Het is ook mogelijk om een algemene verzameling te gebruiken, IDictionary<string, object>als metagegevensweergave, maar dit voorkomt de voordelen van typecontrole en moet worden vermeden.
Normaal gesproken zijn alle eigenschappen die in de metagegevensweergave worden genoemd vereist, en alle exports die deze niet bevatten, worden niet beschouwd als een overeenkomst. Het DefaultValue kenmerk geeft aan dat een eigenschap optioneel is. Als de eigenschap niet is opgenomen, wordt de standaardwaarde toegewezen die is opgegeven als parameter van DefaultValue. Hieronder ziet u twee verschillende klassen met metagegevens. Beide klassen komen overeen met de vorige metagegevensweergave.
<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
{
}
Metagegevens worden na het Export kenmerk uitgedrukt met behulp van het ExportMetadata kenmerk. Elk stukje metagegevens bestaat uit een naam/waardepaar. Het naamgedeelte van de metagegevens moet overeenkomen met de naam van de juiste eigenschap in de metagegevensweergave en de waarde wordt toegewezen aan die eigenschap.
Het is de importeur die aangeeft welke metagegevensweergave, indien van toepassing, wordt gebruikt. Een import met metagegevens wordt gedeclareerd als een luie import, met de metagegevensinterface als de tweede typeparameter voor Lazy<T,T>. Met de volgende klasse wordt het vorige deel geïmporteerd met metagegevens.
Public Class Addin
<Import()>
Public Property plugin As Lazy(Of IPlugin, IPluginMetadata)
End Class
public class Addin
{
[Import]
public Lazy<IPlugin, IPluginMetadata> plugin;
}
In veel gevallen wilt u metagegevens combineren met het ImportMany kenmerk, om de beschikbare importbewerkingen te parseren en slechts één verzameling te kiezen en te instantiëren, of een verzameling te filteren die overeenkomt met een bepaalde voorwaarde. Met de volgende klasse worden alleen IPlugin objecten geïnstitueert die de Name waarde Logger hebben.
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;
}
}
Overname importeren en exporteren
Als een klasse een onderdeel over neemt, kan die klasse ook een onderdeel worden. Import wordt altijd overgenomen door subklassen. Daarom is een subklasse van een onderdeel altijd een onderdeel, met dezelfde importen als de bovenliggende klasse.
Exports die zijn gedeclareerd met behulp van het Export kenmerk, worden niet overgenomen door subklassen. Een onderdeel kan echter zelf exporteren met behulp van het InheritedExport kenmerk. Subklassen van het onderdeel nemen dezelfde export over, inclusief contractnaam en contracttype. In tegenstelling tot een Export kenmerk kan InheritedExport alleen worden toegepast op klasseniveau en niet op lidniveau. Export op lidniveau kan daarom nooit worden overgenomen.
In de volgende vier klassen worden de principes van de overname van import en export gedemonstreerd.
NumTwo erft van NumOne, dus NumTwo importeert IMyData. Gewone exports worden niet overgenomen, dus NumTwo exporteert niets.
NumFour neemt over van NumThree. Omdat NumThree gebruikt InheritedExport, NumFour heeft één export met contracttype NumThree. Export op lidniveau wordt nooit overgenomen, dus IMyData niet geëxporteerd.
<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.
}
Als er metagegevens zijn gekoppeld aan een InheritedExport kenmerk, worden die metagegevens ook overgenomen. (Zie de eerdere sectie Metagegevens en metagegevensweergaven voor meer informatie.) Overgenomen metagegevens kunnen niet worden gewijzigd door de subklasse. Door het InheritedExport kenmerk echter opnieuw te declareren met dezelfde contractnaam en hetzelfde contracttype, maar door nieuwe metagegevens, kan de subklasse de overgenomen metagegevens vervangen door nieuwe metagegevens. In de volgende klasse ziet u dit principe. Het MegaLogger onderdeel neemt over van Logger en bevat het InheritedExport kenmerk. Omdat MegaLogger nieuwe metagegevens met de naam Status herdefinieert, erft het geen naam- en versiegegevens van 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.
}
Wanneer u het InheritedExport kenmerk opnieuw declareren om metagegevens te overschrijven, moet u ervoor zorgen dat de contracttypen hetzelfde zijn. (In het vorige voorbeeld IPlugin is het contracttype.) Als deze verschillen, in plaats van te overschrijven, maakt het tweede kenmerk een tweede, onafhankelijke export van het onderdeel. Over het algemeen betekent dit dat u het contracttype expliciet moet opgeven wanneer u een InheritedExport kenmerk overschrijft, zoals wordt weergegeven in het vorige voorbeeld.
Omdat interfaces niet rechtstreeks kunnen worden geïnstantieerd, kunnen ze over het algemeen niet worden ingericht met Export of Import kenmerken. Een interface kan echter worden ingericht met een InheritedExport kenmerk op interfaceniveau en dat exporteren samen met alle bijbehorende metagegevens wordt overgenomen door alle implementatieklassen. De interface zelf is echter niet beschikbaar als onderdeel.
Aangepaste exportkenmerken
De basisexportkenmerken Export en InheritedExportkunnen worden uitgebreid om metagegevens als kenmerkeigenschappen op te nemen. Deze techniek is handig voor het toepassen van vergelijkbare metagegevens op veel onderdelen of het maken van een overnamestructuur van metagegevenskenmerken.
Een aangepast kenmerk kan het contracttype, de contractnaam of andere metagegevens opgeven. Om een aangepast kenmerk te definiëren, moet een klasse die erft van ExportAttribute (of InheritedExportAttribute) worden gedecoreerd met het MetadataAttribute kenmerk. De volgende klasse definieert een aangepast kenmerk.
<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; }
}
Deze klasse definieert een aangepast kenmerk met de naam MyAttribute contracttype IMyAddin en enkele metagegevens met de naam MyMetadata. Alle eigenschappen in een klasse die zijn gemarkeerd met het MetadataAttribute kenmerk, worden beschouwd als metagegevens die zijn gedefinieerd in het aangepaste kenmerk. De volgende twee declaraties zijn gelijkwaardig.
<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; }
In de eerste declaratie worden het contracttype en de metagegevens expliciet gedefinieerd. In de tweede declaratie zijn het contracttype en de metagegevens impliciet in het aangepaste kenmerk. Met name in gevallen waarin een grote hoeveelheid identieke metagegevens moet worden toegepast op veel onderdelen (bijvoorbeeld auteurs- of copyrightgegevens), kan het gebruik van een aangepast kenmerk veel tijd en duplicatie besparen. Verder kunnen overnamestructuren van aangepaste kenmerken worden gemaakt om variaties mogelijk te maken.
Als u optionele metagegevens in een aangepast kenmerk wilt maken, kunt u het DefaultValue kenmerk gebruiken. Wanneer dit kenmerk wordt toegepast op een eigenschap in een aangepaste kenmerkklasse, geeft deze aan dat de versierde eigenschap optioneel is en niet hoeft te worden geleverd door een exporteur. Als er geen waarde voor de eigenschap wordt opgegeven, wordt de standaardwaarde toegewezen voor het eigenschapstype (meestal null, falseof 0).)
Het beleid voor creatie
Wanneer een onderdeel een import aangeeft en er een samenstellingsbewerking plaatsvindt, probeert de samenstellingscontainer een overeenkomende export te vinden. Als het importeren succesvol overeenkomt met een export, wordt het importlid ingesteld op een exemplaar van het geëxporteerde object. Waar dit exemplaar vandaan komt, wordt bepaald door het creatiebeleid van het onderdeel dat exporteert.
De twee mogelijke maakbeleidsopties zijn gedeeld en niet gedeeld. Een onderdeel met een creatiebeleid om te delen, zal tussen elke import in de container worden gedeeld voor een onderdeel dat onder dat contract valt. Wanneer de samenstellingsengine een overeenkomst vindt en een importeigenschap moet instellen, wordt er alleen een nieuwe kopie van het onderdeel geïnstitueert als deze nog niet bestaat; anders wordt de bestaande kopie opgegeven. Dit betekent dat veel objecten verwijzingen naar hetzelfde onderdeel kunnen hebben. Dergelijke onderdelen mogen niet afhankelijk zijn van de interne toestand die van veel plaatsen kan worden gewijzigd. Dit beleid is geschikt voor statische onderdelen, onderdelen die services leveren en onderdelen die veel geheugen of andere resources verbruiken.
Telkens wanneer een overeenkomende import voor een van zijn exporten wordt gevonden, zal er een onderdeel gecreëerd worden volgens het niet-gedeelde creatiebeleid. Een nieuwe kopie wordt daarom geïnstantieerd voor elke import in de container die overeenkomt met een van de geëxporteerde contracten van het onderdeel. De interne status van deze kopieën wordt niet gedeeld. Dit beleid is geschikt voor onderdelen waarvoor elke import een eigen interne status vereist.
Zowel het importeren als het exporteren kunnen het beleid voor het maken van een onderdeel opgeven, van de waarden Shared, NonShared of Any. De standaardwaarde is Any voor zowel import als export. Een export die Shared of NonShared specificeert, komt alleen overeen met een import die hetzelfde specificeert of die Any specificeert. Op dezelfde manier komt een import die Shared of NonShared aangeeft alleen overeen met een export die ofwel hetzelfde aangeeft, dan wel Any aangeeft. Import en export met een incompatibele aanmaakbeleid worden niet als overeenkomst beschouwd, op dezelfde manier als een import en export waarvan de contractnaam of contracttype niet overeenkomen. Als zowel importeren als exporteren Anyspecificeren, of geen beleidsregel voor aanmaak opgeven en standaard instellen op Any, wordt het aanmaakbeleid standaard gedeeld.
In het volgende voorbeeld worden zowel import- als exportregels weergegeven die specificeren hoe het creëren moet worden aangepakt.
PartOne geeft geen beleid voor maken op, dus de standaardwaarde is Any.
PartTwo geeft geen beleid voor maken op, dus de standaardwaarde is Any. Omdat zowel importeren als exporteren standaard naar Any staat ingesteld, zal PartOne gedeeld worden.
PartThree geeft een Shared beleid voor aanmaak op, dus PartTwo en PartThree zullen hetzelfde exemplaar van PartOne delen.
PartFour hiermee specificeert u een NonShared aanmaakbeleid, waardoor PartFour niet gedeeld zal worden in PartFive.
PartSix specificeert een NonShared maakbeleid.
PartFive en PartSix ontvangt elk afzonderlijke exemplaren van PartFour.
PartSeven specificeert een Shared maakbeleid. Omdat er geen geëxporteerd PartFour is met een aanmaakbeleid van Shared, komt de PartSeven-import met niets overeen en zal niet worden ingevuld.
<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.
}
Levenscyclus en afvoer
Omdat onderdelen worden gehost in de samenstellingscontainer, kan hun levenscyclus complexer zijn dan gewone objecten. Onderdelen kunnen twee belangrijke levenscyclusgerelateerde interfaces implementeren: IDisposable en IPartImportsSatisfiedNotification.
Onderdelen waarvoor werk moet worden uitgevoerd tijdens het afsluiten of die resources moeten vrijgeven, moeten IDisposable implementeren, zoals gebruikelijk voor .NET objecten. Aangezien de container echter verwijzingen naar onderdelen maakt en onderhoudt, moet alleen de container die eigenaar is van een onderdeel de Dispose methode aanroepen. De container implementeert zelf IDisposable en als onderdeel van zijn opschoonproces in Dispose roept hij Dispose aan op alle onderdelen waarvan hij eigenaar is. Daarom moet u altijd de container voor samenstellingen verwijderen wanneer deze en onderdelen waarvan het de eigenaar is niet meer nodig zijn.
Voor samenstellingscontainers die lang meegaan, kan het geheugenverbruik door onderdelen met een creatiebeleid dat niet wordt gedeeld een probleem worden. Deze niet-gedeelde onderdelen kunnen meerdere keren worden gemaakt en worden pas verwijderd nadat de container zelf is verwijderd. Om hiermee om te gaan, biedt de container de ReleaseExport methode. Wanneer deze methode wordt aangeroepen op een niet-gedeelde export, wordt deze uit de samenstellingscontainer verwijderd en afgehandeld. Onderdelen die alleen worden gebruikt door de gedeactiveerde export, enzovoort, worden ook verwijderd en afgevoerd. Op deze manier kunnen middelen worden vrijgemaakt zonder de samenstellingscontainer zelf weg te gooien.
IPartImportsSatisfiedNotification bevat één methode met de naam OnImportsSatisfied. Deze methode wordt aangeroepen door de samenstellingscontainer op onderdelen die de interface implementeren wanneer de samenstelling is voltooid en de import van het onderdeel gereed is voor gebruik. Onderdelen worden gemaakt door de samenstellingsmotor om de invoer van andere onderdelen te vullen. Voordat de import van een onderdeel is ingesteld, kunt u geen initialisatie uitvoeren die afhankelijk is van geïmporteerde waarden in de onderdeelconstructor of bewerkt, tenzij deze waarden zijn opgegeven als vereisten met behulp van het ImportingConstructor kenmerk. Dit is normaal gesproken de voorkeursmethode, maar in sommige gevallen is constructorinjectie mogelijk niet beschikbaar. In dergelijke gevallen kan initialisatie worden uitgevoerd in OnImportsSatisfied, en moet het onderdeel worden geïmplementeerd in IPartImportsSatisfiedNotification.