Managed Extensibility Framework (MEF)Managed Extensibility Framework (MEF)

Cette rubrique fournit une vue d'ensemble de Managed Extensibility Framework, qui a été introduit dans le .NET Framework 4.This topic provides an overview of the Managed Extensibility Framework that was introduced in the .NET Framework 4.

Présentation de MEFWhat is MEF?

Managed Extensibility Framework (ou MEF) est une bibliothèque destinée à la création d'applications simples et extensibles.The Managed Extensibility Framework or MEF is a library for creating lightweight, extensible applications. Elle permet aux développeurs d'applications de découvrir et d'utiliser des extensions sans nécessiter de configuration particulière.It allows application developers to discover and use extensions with no configuration required. Elle permet également aux développeurs d'extensions d'encapsuler du code facilement tout en évitant les dépendances dures et fragiles.It also lets extension developers easily encapsulate code and avoid fragile hard dependencies. De plus, MEF permet de réutiliser des extensions dans une application, mais aussi d'une application à une autre.MEF not only allows extensions to be reused within applications, but across applications as well.

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.

Face à ce problème, l'approche la plus simple consiste à inclure les composants sous forme de code source dans votre application et à les appeler directement à partir de votre code.The simplest approach to the problem is to include the components as source code in your application, and call them directly from your code. Cette approche comporte plusieurs inconvénients évidents.This has a number of obvious drawbacks. L'inconvénient majeur est que vous ne pouvez pas ajouter de nouveaux composants sans modifier le code source. Cette restriction est acceptable dans une application web par exemple, mais elle est rédhibitoire dans une application cliente.Most importantly, you cannot add new components without modifying the source code, a restriction that might be acceptable in, for example, a Web application, but is unworkable in a client application. Autre inconvénient tout aussi problématique, vous risquez de ne pas avoir accès au code source des composants développés par des tiers et, pour la même raison, vous ne pouvez pas leur permettre d'accéder aux vôtres.Equally problematic, you may not have access to the source code for the components, because they might be developed by third parties, and for the same reason you cannot allow them to access yours.

Il existe une approche un peu plus élaborée qui consiste à fournir une interface ou un point d'extension pour découpler l'application de ses composants.A slightly more sophisticated approach would be to provide an extension point or interface, to permit decoupling between the application and its components. Dans ce modèle, vous pouvez fournir une interface à implémenter par un composant, et une API pour lui permettre d'interagir avec votre application.Under this model, you might provide an interface that a component can implement, and an API to enable it to interact with your application. Si cette approche résout le problème de l'accès au code source, elle a d'autres inconvénients spécifiques.This solves the problem of requiring source code access, but it still has its own difficulties.

Comme l'application n'est pas en mesure de découvrir les composants elle-même, vous devez encore lui indiquer explicitement quels composants sont disponibles et doivent être chargés.Because the application lacks any capacity for discovering components on its own, it must still be explicitly told which components are available and should be loaded. Cela peut généralement se faire en inscrivant explicitement les composants disponibles dans un fichier de configuration.This is typically accomplished by explicitly registering the available components in a configuration file. Cela implique de vérifier que les composants sont corrects, ce qui complique la maintenance, en particulier si la mise à jour doit normalement être effectuée par l'utilisateur final et non le développeur.This means that assuring that the components are correct becomes a maintenance issue, particularly if it is the end user and not the developer who is expected to do the updating.

De plus, les composants sont incapables de communiquer entre eux, sauf via les canaux rigides de l'application elle-même.In addition, components are incapable of communicating with one another, except through the rigidly defined channels of the application itself. Si l‘architecte de l‘application n‘a pas anticipé la nécessité d‘établir une communication spécifique, la communication est généralement impossible.If the application architect has not anticipated the need for a particular communication, it is usually impossible.

Enfin, les développeurs de composants doivent accepter la présence d'une dépendance dure sur l'assembly contenant l'interface qu'ils implémentent.Finally, the component developers must accept a hard dependency on what assembly contains the interface they implement. Cette dépendance rend compliquée l'utilisation d'un composant dans plusieurs applications et peut également poser des problèmes quand vous créez une infrastructure de tests pour les composants.This makes it difficult for a component to be used in more than one application, and can also create problems when you create a test framework for components.

Possibilités offertes par MEFWhat MEF Provides

Au lieu de cette inscription explicite des composants disponibles, MEF permet leur découverte implicite par la composition.Instead of this explicit registration of available components, MEF provides a way to discover them implicitly, via composition. Un composant MEF, appelé partie, spécifie de manière déclarative à la fois ses dépendances (les importations) et les fonctionnalités (les exportations) qu’il rend disponibles.A MEF component, called a part, declaratively specifies both its dependencies (known as imports) and what capabilities (known as exports) it makes available. Lorsqu'une partie est créée, le moteur de composition MEF fournit à ses importations les éléments disponibles des autres parties.When a part is created, the MEF composition engine satisfies its imports with what is available from other parts.

Cette approche résout les problèmes abordés dans la section précédente.This approach solves the problems discussed in the previous section. Étant donné que les parties MEF spécifient leurs fonctionnalités de façon déclarative, elles sont détectables au moment de l'exécution, ce qui signifie qu'une application peut utiliser des parties sans avoir recours à des références codées en dur ni à des fichiers de configuration fragiles.Because MEF parts declaratively specify their capabilities, they are discoverable at runtime, which means an application can make use of parts without either hard-coded references or fragile configuration files. MEF permet aux applications de découvrir et d'examiner des parties grâce à leurs métadonnées, sans les instancier ni même charger leurs assemblys.MEF allows applications to discover and examine parts by their metadata, without instantiating them or even loading their assemblies. Par conséquent, il est inutile de spécifier avec précision quand et comment les extensions doivent être chargées.As a result, there is no need to carefully specify when and how extensions should be loaded.

En plus de spécifier les exportations qui lui sont fournies, une partie peut spécifier ses importations, qui seront remplies par d'autres parties.In addition to its provided exports, a part can specify its imports, which will be filled by other parts. Cela permet et facilite la communication entre les parties, tout en garantissant une bonne factorisation du code.This makes communication among parts not only possible, but easy, and allows for good factoring of code. Par exemple, les services communs à de nombreux composants peuvent être factorisés dans une partie distincte et être facilement modifiés ou remplacés.For example, services common to many components can be factored into a separate part and easily modified or replaced.

Étant donné que le modèle MEF n'a besoin d'aucune dépendance dure sur un assembly d'application particulier, il permet la réutilisation des extensions dans d'autres applications.Because the MEF model requires no hard dependency on a particular application assembly, it allows extensions to be reused from application to application. Cela facilite également le développement d’un atelier de test, indépendant de l’application, pour tester les composants d’extension.This also makes it easy to develop a test harness, independent of the application, to test extension components.

Une application extensible écrite à l'aide de MEF déclare une importation qui peut être remplie par les composants d'extension, et peut aussi déclarer des exportations pour exposer ses services aux extensions.An extensible application written by using MEF declares an import that can be filled by extension components, and may also declare exports in order to expose application services to extensions. Chaque composant d’extension déclare une exportation et éventuellement des importations.Each extension component declares an export, and may also declare imports. De cette façon, les composants d'extension eux-mêmes sont automatiquement extensibles.In this way, extension components themselves are automatically extensible.

Où se procurer MEF ?Where Is MEF Available?

MEF fait partie intégrante du .NET Framework 4.NET Framework 4 et est disponible partout où le .NET Framework est utilisé.MEF is an integral part of the .NET Framework 4.NET Framework 4, and is available wherever the .NET Framework is used. Vous pouvez utiliser MEF dans vos applications clientes, si elles utilisent des Windows Forms, WPF ou une autre technologie, ou dans les applications serveur ASP.NET.You can use MEF in your client applications, whether they use Windows Forms, WPF, or any other technology, or in server applications that use ASP.NET.

MEF et MAFMEF and MAF

Les versions précédentes du .NET Framework ont introduit Managed Add-in Framework (MAF), une infrastructure conçue pour permettre aux applications d’isoler et de gérer des extensions.Previous versions of the .NET Framework introduced the Managed Add-in Framework (MAF), designed to allow applications to isolate and manage extensions. Le focus de MAF est légèrement plus général que MEF : MAF se concentre sur l’isolement des extensions et le chargement/déchargement des assemblys, tandis que le focus de MEF englobe la détectabilité, l’extensibilité et la portabilité.The focus of MAF is slightly higher-level than MEF, concentrating on extension isolation and assembly loading and unloading, while MEF's focus is on discoverability, extensibility, and portability. Les deux infrastructures interagissent de façon transparente et peuvent être utilisées par une même application.The two frameworks interoperate smoothly, and a single application can take advantage of both.

SimpleCalculator : exemple d'applicationSimpleCalculator: An Example Application

Le meilleur moyen de découvrir les possibilités de MEF consiste à créer une application MEF simple.The simplest way to see what MEF can do is to build a simple MEF application. Dans cet exemple, vous allez créer une calculatrice très simple nommée SimpleCalculator.In this example, you build a very simple calculator named SimpleCalculator. L'objectif de SimpleCalculator est de créer une application console qui accepte des commandes arithmétiques de base (de type « 5 + 3 » ou « 6 - 2 ») et retourne des résultats corrects.The goal of SimpleCalculator is to create a console application that accepts basic arithmetic commands, in the form "5+3" or "6-2", and returns the correct answers. Grâce à MEF, vous pourrez ajouter de nouveaux opérateurs sans avoir à modifier le code de l'application.Using MEF, you'll be able to add new operators without changing the application code.

Pour télécharger le code complet de cet exemple, consultez l’exemple SimpleCalculator.To download the complete code for this example, see the SimpleCalculator sample.

Note

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.

  • Pour commencer, dans Visual Studio, créez un projet Application console et nommez-le SimpleCalculator.To start, in Visual Studio, create a new Console Application project and name it SimpleCalculator.

  • Ajoutez une référence à l'assembly System.ComponentModel.Composition, dans lequel réside MEF.Add a reference to the System.ComponentModel.Composition assembly, where MEF resides.

  • Ouvrez Module1.vb ou Program.cs et ajoutez des instructions Imports ou using pour System.ComponentModel.Composition et System.ComponentModel.Composition.Hosting.Open Module1.vb or Program.cs and add Imports or using statements for System.ComponentModel.Composition and System.ComponentModel.Composition.Hosting. Ces deux espaces de noms contiennent des types MEF dont vous avez besoin pour développer une application extensible.These two namespaces contain MEF types you will need to develop an extensible application.

  • Si vous utilisez Visual Basic, ajoutez le mot clé Public dans la ligne qui déclare le module Module1.If you're using Visual Basic, add the Public keyword to the line that declares the Module1 module.

Catalogues et conteneur de compositionComposition Container and Catalogs

Le cœur du modèle de composition MEF est le conteneur de composition, qui contient toutes les parties disponibles et exécute la composition.The core of the MEF composition model is the composition container, which contains all the parts available and performs composition. La composition est la correspondance entre les importations et les exportations.Composition is the matching up of imports to exports. Pour SimpleCalculator, vous allez utiliser CompositionContainer, qui est le type de conteneur de composition le plus courant.The most common type of composition container is CompositionContainer, and you'll use this for SimpleCalculator.

Si vous utilisez Visual Basic, dans Module1.vb, ajoutez une classe publique nommée Program.If you're using Visual Basic, in Module1.vb, add a public class named Program.

Ajoutez la ligne suivante à la classe Program dans Module1.vb ou Program.cs :Add the following line to the Program class in Module1.vb or Program.cs:

Dim _container As CompositionContainer
private CompositionContainer _container;

Pour découvrir les parties disponibles, les conteneurs de composition utilisent un catalogue.In order to discover the parts available to it, the composition containers makes use of a catalog. Un catalogue est un objet qui rend disponibles les parties découvertes dans une source.A catalog is an object that makes available parts discovered from some source. MEF propose des catalogues pour la découverte des parties dans un type, un assembly ou un répertoire fourni.MEF provides catalogs to discover parts from a provided type, an assembly, or a directory. Les développeurs d'applications peuvent facilement créer des catalogues pour découvrir des parties dans d'autres sources, telles qu'un service web.Application developers can easily create new catalogs to discover parts from other sources, such as a Web service.

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.

Importations et exportations avec des attributsImports and Exports with Attributes

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.)

Ajoutez cette interface très simple au module ou à l'espace de noms SimpleCalculator :Add this very simple interface to the module or SimpleCalculator namespace:

Public Interface ICalculator
    Function Calculate(ByVal input As String) As String
End Interface
public interface ICalculator
{
    String Calculate(String input);
}

Vous venez de définir ICalculator, vous avez besoin maintenant d'une classe qui l'implémente.Now that you have defined ICalculator, you need a class that implements it. Ajoutez la classe suivante au module ou à l'espace de noms SimpleCalculator :Add the following class to the module or SimpleCalculator namespace:

<Export(GetType(ICalculator))>
Public Class MySimpleCalculator
   Implements ICalculator

End Class
[Export(typeof(ICalculator))]
class MySimpleCalculator : ICalculator
{

}

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.

La couche d'interface utilisateur (Program) n'a besoin d'aucune autre information.The user interface layer (Program) does not need to know anything else. Vous pouvez donc remplir le reste de la logique d'interface utilisateur dans la méthode Main.You can therefore fill in the rest of the user interface logic in the Main method.

Ajoutez le code suivant à la méthode Main :Add the following code to the Main method:

Sub Main()
    Dim p As New Program()
    Dim s As String
    Console.WriteLine("Enter Command:")
    While (True)
        s = Console.ReadLine()
        Console.WriteLine(p.calculator.Calculate(s))
    End While
End Sub
static void Main(string[] args)
{
    Program p = new Program(); //Composition is performed in the constructor
    String s;
    Console.WriteLine("Enter Command:");
    while (true)
    {
        s = Console.ReadLine();
        Console.WriteLine(p.calculator.Calculate(s));
    }
}

Ce code lit simplement une ligne d'entrée et appelle la fonction Calculate de l'interface ICalculator dans le résultat, qu'il réécrit dans la console.This code simply reads a line of input and calls the Calculate function of ICalculator on the result, which it writes back to the console. C'est le seul code dont vous avez besoin dans Program.That is all the code you need in Program. Tout le reste du processus s'effectuera dans les parties.All the rest of the work will happen in the parts.

Importations supplémentaires et ImportManyFurther Imports and ImportMany

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.

Ajoutez la propriété operations suivante à la classe MySimpleCalculator :Add the following operations property to the MySimpleCalculator class:

<ImportMany()>
Public Property operations As IEnumerable(Of Lazy(Of IOperation, IOperationData))
[ImportMany]
IEnumerable<Lazy<IOperation, IOperationData>> operations;

Lazy<T,TMetadata> est un type fourni par MEF pour contenir des références indirectes à des exportations.Lazy<T,TMetadata> is a type provided by MEF to hold indirect references to exports. Ici, en plus de l’objet exporté lui-même, vous obtenez des métadonnées d’exportation, ou des informations qui décrivent l’objet exporté.Here, in addition to the exported object itself, you also get export metadata, or information that describes the exported object. Chaque Lazy<T,TMetadata> contient un objet IOperation représentant une opération réelle et un objet IOperationData représentant ses métadonnées.Each Lazy<T,TMetadata> contains an IOperation object, representing an actual operation, and an IOperationData object, representing its metadata.

Ajoutez les interfaces simples suivantes au module ou à l'espace de noms SimpleCalculator :Add the following simple interfaces to the module or SimpleCalculator namespace:

Public Interface IOperation
    Function Operate(ByVal left As Integer, ByVal right As Integer) As Integer
End Interface

Public Interface IOperationData
    ReadOnly Property Symbol As Char
End Interface
public interface IOperation
{
     int Operate(int left, int right);
}

public interface IOperationData
{
    Char Symbol { get; }
}

Dans ce cas, les métadonnées de chaque opération correspondent au symbole représentant cette opération (par exemple, +, -, *, etc.).In this case, the metadata for each operation is the symbol that represents that operation, such as +, -, *, and so on. Pour rendre l'addition disponible, ajoutez la classe suivante au module ou à l'espace de noms SimpleCalculator :To make the addition operation available, add the following class to the module or SimpleCalculator namespace:

<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
[Export(typeof(IOperation))]
[ExportMetadata("Symbol", '+')]
class Add: IOperation
{
    public int Operate(int left, int right)
    {
        return left + right;
    }
}

L'attribut ExportAttribute fonctionne comme auparavant.The ExportAttribute attribute functions as it did before. L'attribut ExportMetadataAttribute joint des métadonnées à cette exportation, sous la forme d'une paire nom-valeur.The ExportMetadataAttribute attribute attaches metadata, in the form of a name-value pair, to that export. La classe Add implémente IOperation, mais aucune classe implémentant IOperationData n'est explicitement définie.While the Add class implements IOperation, a class that implements IOperationData is not explicitly defined. En fait, MEF crée implicitement une classe en lui attribuant des propriétés basées sur les noms des métadonnées fournies.Instead, a class is implicitly created by MEF with properties based on the names of the metadata provided. (C'est l'une des différentes méthodes permettant d'accéder aux métadonnées dans MEF.)(This is one of several ways to access metadata in MEF.)

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.)

Logique de la calculatriceCalculator Logic

Après avoir mis en place les différentes parties, il reste simplement à définir la logique de la calculatrice elle-même.With these parts in place, all that remains is the calculator logic itself. Ajoutez le code suivant à la classe MySimpleCalculator pour implémenter la méthode Calculate :Add the following code in the MySimpleCalculator class to implement the Calculate method:

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
public String Calculate(String input)
{
    int left;
    int right;
    Char operation;
    int fn = FindFirstNonDigit(input); //finds the operator
    if (fn < 0) return "Could not parse command.";

    try
    {
        //separate out the operands
        left = int.Parse(input.Substring(0, fn));
        right = int.Parse(input.Substring(fn + 1));
    }
    catch
    {
        return "Could not parse command.";
    }

    operation = input[fn];

    foreach (Lazy<IOperation, IOperationData> i in operations)
    {
        if (i.Metadata.Symbol.Equals(operation)) return i.Value.Operate(left, right).ToString();
    }
    return "Operation Not Found!";
}

Les étapes initiales analysent la chaîne d'entrée dans les opérandes de gauche et de droite, et un caractère d'opérateur.The initial steps parse the input string into left and right operands and an operator character. Dans la boucle foreach, chaque membre de la collection operations est examiné.In the foreach loop, every member of the operations collection is examined. Ces objets sont de type Lazy<T,TMetadata>, et leurs valeurs de métadonnées et d'objet exporté sont accessibles respectivement via la propriété Metadata et la propriété Value.These objects are of type Lazy<T,TMetadata>, and their metadata values and exported object can be accessed with the Metadata property and the Value property respectively. Dans ce cas, si la propriété Symbol découverte de l'objet IOperationData est une correspondance, la calculatrice appelle la méthode Operate de l'objet IOperation et retourne le résultat.In this case, if the Symbol property of the IOperationData object is discovered to be a match, the calculator calls the Operate method of the IOperation object and returns the result.

Pour terminer la calculatrice, vous devez également définir une méthode d'assistance qui retourne la position du premier caractère non numérique dans une chaîne.To complete the calculator, you also need a helper method that returns the position of the first non-digit character in a string. Ajoutez la méthode d'assistance suivante à la classe MySimpleCalculator :Add the following helper method to the MySimpleCalculator class:

Private Function FindFirstNonDigit(ByVal s As String) As Integer
    For i = 0 To s.Length
        If (Not (Char.IsDigit(s(i)))) Then Return i
    Next
    Return -1
End Function
private int FindFirstNonDigit(String s)
{
    for (int i = 0; i < s.Length; i++)
    {
        if (!(Char.IsDigit(s[i]))) return i;
    }
    return -1;
}

Vous devez maintenant être en mesure de compiler et d'exécuter le projet.You should now be able to compile and run the project. Dans Visual Basic, assurez-vous d'avoir ajouté le mot clé Public au module Module1.In Visual Basic, make sure that you added the Public keyword to Module1. Dans la fenêtre de console, tapez une addition, par exemple « 5 + 3 », et la calculatrice retourne le résultat.In the console window, type an addition operation, such as "5+3", and the calculator returns the results. L’utilisation d’un autre opérateur entraîne l’affichage du messageAny other operator results in the "Operation Not Found!" « Operation Not Found! ».message.

Extension de SimpleCalculator à l'aide d'une nouvelle classeExtending SimpleCalculator Using A New Class

La calculatrice fonctionne. Vous pouvez maintenant ajouter facilement une nouvelle opération.Now that the calculator works, adding a new operation is easy. Ajoutez la classe suivante au module ou à l'espace de noms SimpleCalculator :Add the following class to the module or SimpleCalculator namespace:

<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(typeof(IOperation))]
[ExportMetadata("Symbol", '-')]
class Subtract : IOperation
{
    public int Operate(int left, int right)
    {
        return left - right;
    }
}

Compilez et exécutez le projet.Compile and run the project. Tapez une soustraction, par exemple « 5-3 ».Type a subtraction operation, such as "5-3". La calculatrice prend désormais en charge la soustraction ainsi que l'addition.The calculator now supports subtraction as well as addition.

Extension de SimpleCalculator à l'aide d'un nouvel assemblyExtending SimpleCalculator Using A New Assembly

L'ajout de classes au code source est une opération relativement simple, mais MEF permet de rechercher des parties en dehors de la source d'une application.Adding classes to the source code is simple enough, but MEF provides the ability to look outside an application’s own source for parts. Pour découvrir cette possibilité, vous devez modifier SimpleCalculator pour qu'il recherche des parties dans un répertoire et dans son propre assembly, en ajoutant un catalogue DirectoryCatalog.To demonstrate this, you will need to modify SimpleCalculator to search a directory, as well as its own assembly, for parts, by adding a DirectoryCatalog.

Ajoutez un nouveau répertoire nommé Extensions au projet SimpleCalculator.Add a new directory named Extensions to the SimpleCalculator project. Assurez-vous de l'ajouter au niveau du projet et pas au niveau de la solution.Make sure to add it at the project level, and not at the solution level. Ajoutez ensuite un nouveau projet de bibliothèque de classes, appelé ExtendedOperations, à la solution.Then add a new Class Library project to the solution, named ExtendedOperations. Le nouveau projet sera compilé dans un assembly distinct.The new project will compile into a separate assembly.

Ouvrez le concepteur des propriétés de projet pour le projet ExtendedOperations, puis cliquez sur l’onglet Compiler ou Générer. Modifiez la valeur Chemin de sortie de la génération ou la valeur Chemin de sortie pour pointer vers le répertoire Extensions dans le répertoire du projet SimpleCalculator (..\SimpleCalculator\Extensions\).Open the Project Properties Designer for the ExtendedOperations project and click the Compile or Build tab. Change the Build output path or Output path to point to the Extensions directory in the SimpleCalculator project directory (..\SimpleCalculator\Extensions\).

Dans Module1.vb ou Program.cs, ajoutez la ligne suivante au constructeur Program :In Module1.vb or Program.cs, add the following line to the Program constructor:

catalog.Catalogs.Add(New DirectoryCatalog("C:\SimpleCalculator\SimpleCalculator\Extensions"))
catalog.Catalogs.Add(new DirectoryCatalog("C:\\SimpleCalculator\\SimpleCalculator\\Extensions"));

Remplacez le chemin d’accès de l’exemple par le chemin d’accès à votre répertoire Extensions.Replace the example path with the path to your Extensions directory. (Ce chemin d'accès absolu sert uniquement au débogage.(This absolute path is for debugging purposes only. Dans une application de production, vous devez utiliser un chemin d'accès relatif.) DirectoryCatalog ajoutera maintenant au conteneur de composition toutes les parties découvertes dans les assemblys du répertoire Extensions.In a production application, you would use a relative path.) The DirectoryCatalog will now add any parts found in any assemblies in the Extensions directory to the composition container.

Dans le projet ExtendedOperations, ajoutez des références à SimpleCalculator et à System.ComponentModel.Composition.In the ExtendedOperations project, add references to SimpleCalculator and System.ComponentModel.Composition. Dans le fichier de classe ExtendedOperations, ajoutez une instruction Imports ou using pour System.ComponentModel.Composition.In the ExtendedOperations class file, add an Imports or a using statement for System.ComponentModel.Composition. Dans Visual Basic, ajoutez également une instruction Imports pour SimpleCalculator.In Visual Basic, also add an Imports statement for SimpleCalculator. Ajoutez ensuite la classe suivante au fichier de classe ExtendedOperations :Then add the following class to the ExtendedOperations class file:

<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
[Export(typeof(SimpleCalculator.IOperation))]
[ExportMetadata("Symbol", '%')]
public class Mod : SimpleCalculator.IOperation
{
    public int Operate(int left, int right)
    {
        return left % right;
    }
}

Notez que l'attribut ExportAttribute doit avoir le même type que ImportAttribute pour que le contrat corresponde.Note that in order for the contract to match, the ExportAttribute attribute must have the same type as the ImportAttribute.

Compilez et exécutez le projet.Compile and run the project. Testez le nouvel opérateur Mod (%).Test the new Mod (%) operator.

ConclusionConclusion

Cette rubrique a couvert les concepts de base de MEF.This topic covered the basic concepts of MEF.

  • Parties, catalogues et conteneur de compositionParts, catalogs, and the composition container

    Les parties et le conteneur de composition sont les blocs de construction élémentaires d'une application MEF.Parts and the composition container are the basic building blocks of a MEF application. Une partie est un objet qui importe ou exporte une valeur, y compris lui-même.A part is any object that imports or exports a value, up to and including itself. Un catalogue fournit une collection de parties disponibles dans une source donnée.A catalog provides a collection of parts from a particular source. Le conteneur de composition utilise les parties fournies par un catalogue pour exécuter la composition, la liaison d’importations à des exportations.The composition container uses the parts provided by a catalog to perform composition, the binding of imports to exports.

  • Importations et exportationsImports and exports

    Les importations et les exportations sont un moyen de communication utilisé par les composants.Imports and exports are the way by which components communicate. Lors d'une importation, le composant spécifie qu'il a besoin d'une valeur ou d'un objet en particulier et, lors d'une exportation, il indique la disponibilité d'une valeur.With an import, the component specifies a need for a particular value or object, and with an export it specifies the availability of a value. Chaque importation est associée à une liste d'exportations par l'intermédiaire de son contrat.Each import is matched with a list of exports by way of its contract.

Informations complémentairesWhere Do I Go Now?

Pour télécharger le code complet de cet exemple, consultez l’exemple SimpleCalculator.To download the complete code for this example, see the SimpleCalculator sample.

Pour obtenir plus d’informations et des exemples de code, consultez Managed Extensibility Framework.For more information and code examples, see Managed Extensibility Framework. Pour obtenir une liste des types MEF, consultez l'espace de noms System.ComponentModel.Composition.For a list of the MEF types, see the System.ComponentModel.Composition namespace.