Problème de l'extensibilitéThe Problem of Extensibility

Imaginez que vous êtes l'architecte d'une grande application devant fournir une prise en charge pour l'extensibilité.Imagine that you are the architect of a large application that must provide support for extensibility.Votre application doit inclure un nombre potentiellement important de petits composants qu'elle est censée créer et exécuter.Your application has to include a potentially large number of smaller components, and is responsible for creating and running them.

L'objectif de SimpleCalculator est avant tout de montrer les concepts et la syntaxe de MEF, et non de fournir un scénario d'utilisation réaliste.The purpose of SimpleCalculator is to demonstrate the concepts and syntax of MEF, rather than to necessarily provide a realistic scenario for its use.La plupart des applications qui tirent le meilleur parti de MEF sont plus complexes que SimpleCalculator.Many of the applications that would benefit most from the power of MEF are more complex than SimpleCalculator.Pour obtenir des exemples plus détaillés, consultez Managed Extensibility Framework sur GitHub.For more extensive examples, see the Managed Extensibility Framework on GitHub.

Ajoutez le constructeur suivant à la classe Program :Add the following constructor to the Program class:

Public Sub New()
'An aggregate catalog that combines multiple catalogs
Dim catalog = New AggregateCatalog()
'Adds all the parts found in the same assembly as the Program class
catalog.Catalogs.Add(New AssemblyCatalog(GetType(Program).Assembly))
'Create the CompositionContainer with the parts in the catalog
_container = New CompositionContainer(catalog)
'Fill the imports of this object
Try
_container.ComposeParts(Me)
Catch ex As Exception
Console.WriteLine(ex.ToString)
End Try
End Sub

private Program()
{
//An aggregate catalog that combines multiple catalogs
var catalog = new AggregateCatalog();
//Adds all the parts found in the same assembly as the Program class
catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
//Create the CompositionContainer with the parts in the catalog
_container = new CompositionContainer(catalog);
//Fill the imports of this object
try
{
this._container.ComposeParts(this);
}
catch (CompositionException compositionException)
{
Console.WriteLine(compositionException.ToString());
}
}

L'appel à ComposeParts indique au conteneur de composition qu'il doit composer un ensemble spécifique de parties (dans ce cas, l'instance actuelle de Program).The call to ComposeParts tells the composition container to compose a specific set of parts, in this case the current instance of Program.Toutefois, rien ne se produira à ce stade, puisque Program n'a pas d'importations à remplir.At this point, however, nothing will happen, since Program has no imports to fill.

Tout d'abord, Program doit importer une calculatrice.First, you have Program import a calculator.Cela permet de séparer les problèmes d'interface utilisateur, comme l'entrée et la sortie console qui iront dans Program, de la logique de la calculatrice.This allows the separation of user interface concerns, such as the console input and output that will go into Program, from the logic of the calculator.

Ajoutez le code suivant à la classe Program :Add the following code to the Program class:

<Import(GetType(ICalculator))>
Public Property calculator As ICalculator

[Import(typeof(ICalculator))]
public ICalculator calculator;

Notez que la déclaration de l'objet calculator n'est pas inhabituelle, mais qu'elle est décorée avec l'attribut ImportAttribute.Notice that the declaration of the calculator object is not unusual, but that it is decorated with the ImportAttribute attribute.Cet attribut déclare qu'un élément est une importation, c'est-à-dire qu'il sera rempli par le moteur de composition quand l'objet sera composé.This attribute declares something to be an import; that is, it will be filled by the composition engine when the object is composed.

Chaque importation a un contrat qui détermine à quelles exportations elle doit être associée.Every import has a contract, which determines what exports it will be matched with.Le contrat peut être une chaîne explicitement spécifiée ou il peut être généré automatiquement par MEF à partir d'un type donné (dans ce cas, l'interface ICalculator).The contract can be an explicitly specified string, or it can be automatically generated by MEF from a given type, in this case the interface ICalculator.Toute exportation déclarée avec un contrat correspondant effectuera cette importation.Any export declared with a matching contract will fulfill this import.Le type de l'objet calculator est ici ICalculator, mais ce n'est pas une obligation.Note that while the type of the calculator object is in fact ICalculator, this is not required.Le contrat est indépendant du type de l'objet d'importation.The contract is independent from the type of the importing object.(Dans ce cas, vous pouvez ignorer typeof(ICalculator).(In this case, you could leave out the typeof(ICalculator).MEF déduira automatiquement que le contrat est basé sur le type de l'importation, sauf si vous spécifiez un type explicitement.)MEF will automatically assume the contract to be based on the type of the import unless you specify it explicitly.)

Voici l'exportation qui correspondra à l'importation dans Program.Here is the export that will match the import in Program.Pour que l'exportation corresponde à l'importation, l'exportation doit avoir le même contrat.In order for the export to match the import, the export must have the same contract.Une exportation avec un contrat basé sur typeof(MySimpleCalculator) engendrerait une incompatibilité, et l'importation ne serait pas remplie ; les contrats doivent correspondre exactement.Exporting under a contract based on typeof(MySimpleCalculator) would produce a mismatch, and the import would not be filled; the contract needs to match exactly.

Comme le conteneur de composition sera rempli avec toutes les parties disponibles dans cet assembly, la partie MySimpleCalculator sera disponible.Since the composition container will be populated with all the parts available in this assembly, the MySimpleCalculator part will be available.Quand le constructeur de Program exécutera la composition sur l'objet Program, son importation sera remplie avec un objet MySimpleCalculator qui sera créé à cet effet.When the constructor for Program performs composition on the Program object, its import will be filled with a MySimpleCalculator object, which will be created for that purpose.

Pour que SimpleCalculator soit extensible, il doit importer une liste d'opérations.In order for SimpleCalculator to be extensible, it needs to import a list of operations.Un attribut ImportAttribute standard est rempli par une seule exportation ExportAttribute.An ordinary ImportAttribute attribute is filled by one and only one ExportAttribute.Si plusieurs exportations sont disponibles, le moteur de composition génère une erreur.If more than one is available, the composition engine produces an error.Pour créer une importation qui peut être remplie par un nombre indéfini d'exportations, utilisez l'attribut ImportManyAttribute.To create an import that can be filled by any number of exports, you can use the ImportManyAttribute attribute.

<Export(GetType(IOperation))>
<ExportMetadata("Symbol", "+"c)>
Public Class Add
Implements IOperation
Public Function Operate(ByVal left As Integer, ByVal right As Integer) As Integer Implements IOperation.Operate
Return left + right
End Function
End Class

La composition dans MEF est récursive.Composition in MEF is recursive.Vous avez composé explicitement l'objet Program, qui a importé un ICalculator de type MySimpleCalculator.You explicitly composed the Program object, which imported an ICalculator that turned out to be of type MySimpleCalculator.Ensuite, MySimpleCalculator importe une collection d'objets IOperation. Cette importation sera remplie quand MySimpleCalculator sera créé, en même temps que les importations de Program.MySimpleCalculator, in turn, imports a collection of IOperation objects, and that import will be filled when MySimpleCalculator is created, at the same time as the imports of Program.Si la classe Add a déclaré une importation supplémentaire, celle-ci devra également être remplie, et ainsi de suite.If the Add class declared a further import, that too would have to be filled, and so on.Une importation non remplie provoque une erreur de composition.Any import left unfilled results in a composition error.(Toutefois, il est possible de déclarer que des importations sont facultatives ou de leur affecter des valeurs par défaut.)(It is possible, however, to declare imports to be optional or to assign them default values.)

Public Function Calculate(ByVal input As String) As String Implements ICalculator.Calculate
Dim left, right As Integer
Dim operation As Char
Dim fn = FindFirstNonDigit(input) 'Finds the operator
If fn < 0 Then
Return "Could not parse command."
End If
operation = input(fn)
Try
left = Integer.Parse(input.Substring(0, fn))
right = Integer.Parse(input.Substring(fn + 1))
Catch ex As Exception
Return "Could not parse command."
End Try
For Each i As Lazy(Of IOperation, IOperationData) In operations
If i.Metadata.symbol = operation Then
Return i.Value.Operate(left, right).ToString()
End If
Next
Return "Operation not found!"
End Function

<Export(GetType(IOperation))>
<ExportMetadata("Symbol", "-"c)>
Public Class Subtract
Implements IOperation
Public Function Operate(ByVal left As Integer, ByVal right As Integer) As Integer Implements IOperation.Operate
Return left - right
End Function
End Class

<Export(GetType(SimpleCalculator.IOperation))>
<ExportMetadata("Symbol", "%"c)>
Public Class Modulo
Implements IOperation
Public Function Operate(ByVal left As Integer, ByVal right As Integer) As Integer Implements IOperation.Operate
Return left Mod right
End Function
End Class