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

Dieses Thema bietet eine Übersicht über das in .NET Framework 4 eingeführte Managed Extensibility Framework.This topic provides an overview of the Managed Extensibility Framework that was introduced in the .NET Framework 4.

Was ist MEF?What is MEF?

Das Managed Extensibility Framework oder MEF ist eine Bibliothek zum Erstellen von einfachen und erweiterbaren Anwendungen.The Managed Extensibility Framework or MEF is a library for creating lightweight, extensible applications. Es ermöglicht Anwendungsentwicklern, Erweiterungen ohne Konfiguration zu ermitteln und zu verwenden.It allows application developers to discover and use extensions with no configuration required. Das Framework ermöglicht Erweiterungsentwicklern, Code leicht zu kapseln und zerbrechliche harte Abhängigkeiten zu vermeiden.It also lets extension developers easily encapsulate code and avoid fragile hard dependencies. MEF ermöglicht nicht nur die Wiederverwendung von Erweiterungen innerhalb von Anwendungen, sondern auch anwendungsübergreifend.MEF not only allows extensions to be reused within applications, but across applications as well.

Das Problem der ErweiterbarkeitThe Problem of Extensibility

Angenommen, Sie entwickeln eine komplexe erweiterungsfähige Anwendung.Imagine that you are the architect of a large application that must provide support for extensibility. Die Anwendung umfasst eine große Anzahl kleinerer Komponenten und ist gleichzeitig für das Erstellen und Ausführen der Komponenten zuständig.Your application has to include a potentially large number of smaller components, and is responsible for creating and running them.

Der einfachste Lösungsansatz ist, die Komponenten als Quellcode in die Anwendung zu integrieren und sie direkt vom Code aufzurufen.The simplest approach to the problem is to include the components as source code in your application, and call them directly from your code. Hierbei ergeben sich allerdings mehrere offensichtliche Nachteile.This has a number of obvious drawbacks. Zunächst einmal können keine neuen Komponenten hinzugefügt werden, ohne dabei den Quellcode zu ändern. Dies wäre zwar für eine Webanwendung, jedoch nicht für eine Clientanwendung akzeptabel.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. Außerdem kann möglicherweise nicht auf den Quellcode der Komponenten zugegriffen werden, da diese eventuell von Drittanbietern entwickelt wurden. Aus dem gleichen Grund darf den Komponenten auch nicht der Zugriff auf Ihren Quellcode gestattet werden.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.

Eine elegantere Möglichkeit wäre die Bereitstellung eines Erweiterungspunkts oder einer Schnittstelle, um die Komponenten von der Anwendung zu entkoppeln.A slightly more sophisticated approach would be to provide an extension point or interface, to permit decoupling between the application and its components. Bei diesem Modell kann eine Schnittstelle zur Implementierung einer Komponente und eine API zur Interaktion mit der Anwendung bereitgestellt werden.Under this model, you might provide an interface that a component can implement, and an API to enable it to interact with your application. So ist die Gewährung von Quellcodezugriff nicht mehr erforderlich, allerdings müssen hier andere Schwierigkeiten beachtet werden.This solves the problem of requiring source code access, but it still has its own difficulties.

Da von der Anwendung Komponenten nicht selbständig ermittelt werden können, müssen die verfügbaren und zu ladenden Komponenten exakt angegeben werden.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. Hierfür werden in der Regel die verfügbaren Komponenten in einer Konfigurationsdatei genau registriert.This is typically accomplished by explicitly registering the available components in a configuration file. Das Gewährleisten der Verwendung der richtigen Komponenten kann also zu einem Wartungsproblem werden, insbesondere, wenn die Aktualisierung vom Endbenutzer und nicht vom Entwickler durchgeführt werden muss.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.

Außerdem können Komponenten nicht untereinander kommunizieren, außer durch die fest definierten Channels der Anwendung selbst.In addition, components are incapable of communicating with one another, except through the rigidly defined channels of the application itself. Wurde vom Anwendungsentwickler eine bestimmte Kommunikation nicht eingeplant, kann sie normalerweise nicht stattfinden.If the application architect has not anticipated the need for a particular communication, it is usually impossible.

Schließlich müssen sich Komponentenentwickler für harte Abhängigkeiten zwischen Assemblys und den implementierten Schnittstellen entscheiden.Finally, the component developers must accept a hard dependency on what assembly contains the interface they implement. Dies kann bei der Verwendung einer Komponente in mehr als einer Anwendung und der Erstellung eines Testframeworks für Komponenten Schwierigkeiten bereiten.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.

Dies wird von MEF bereitgestelltWhat MEF Provides

Mit MEF ist die explizite Registrierung verfügbarer Komponenten nicht mehr erforderlich, da sie mithilfe von Komposition implizit ermittelt werden können.Instead of this explicit registration of available components, MEF provides a way to discover them implicitly, via composition. Durch eine MEF-Komponente (Teil) werden sowohl die Abhängigkeiten (Importe) als auch die verfügbaren Funktionen (Exporte) deklarativ angegeben.A MEF component, called a part, declaratively specifies both its dependencies (known as imports) and what capabilities (known as exports) it makes available. Bei der Erstellung eines Teils werden von der MEF-Kompositions-Engine die von anderen Teilen verfügbaren Komponenten für die Importe bereitgestellt.When a part is created, the MEF composition engine satisfies its imports with what is available from other parts.

Auf diese Weise können die im vorherigen Abschnitt erläuterten Probleme vermieden werden.This approach solves the problems discussed in the previous section. Da die Funktionen von den MEF-Teilen deklarativ angegeben werden, sind sie zur Laufzeit auffindbar. Eine Anwendung kann Teile also ohne hartcodierte Verweise oder instabile Konfigurationsdateien nutzen.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. Mit dem MEF können Anwendungen Teile anhand von Metadaten ermitteln und untersuchen. Dabei müssen die Teile nicht instanziiert oder ihre Assemblys geladen werden.MEF allows applications to discover and examine parts by their metadata, without instantiating them or even loading their assemblies. Daher muss nicht genau angegeben werden, wann und auf welche Weise Erweiterungen geladen werden sollen.As a result, there is no need to carefully specify when and how extensions should be loaded.

Zusätzlich zu den bereitgestellten Exporten können von einem Teil seine Importe angegeben werden, die von anderen Teilen ausgefüllt werden.In addition to its provided exports, a part can specify its imports, which will be filled by other parts. Dies ermöglicht die einfache Kommunikation zwischen Teilen und die optimale Verarbeitung von Code.This makes communication among parts not only possible, but easy, and allows for good factoring of code. Gemeinsame Dienste von Komponenten können z. B. separat aufgeteilt und leicht geändert oder ersetzt werden.For example, services common to many components can be factored into a separate part and easily modified or replaced.

Da im MEF-Modell keine harten Abhängigkeiten für Anwendungsassemblys erforderlich sind, können Erweiterungen in mehreren Anwendungen wiederverwendet werden.Because the MEF model requires no hard dependency on a particular application assembly, it allows extensions to be reused from application to application. So können anwendungsunabhängig auch mühelos Testumgebungen für Erweiterungskomponenten entwickelt werden.This also makes it easy to develop a test harness, independent of the application, to test extension components.

Eine erweiterbare, mithilfe des MEF erstellte Anwendung deklariert einen Import, der von Erweiterungskomponenten ausgefüllt werden kann und kann ebenfalls Exporte deklarieren, um Anwendungsdienste für Erweiterungen verfügbar zu machen.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. Von jeder Erweiterungskomponente wird ein Export deklariert. Importe können ebenfalls deklariert werden.Each extension component declares an export, and may also declare imports. Auf diese Weise sind Erweiterungskomponenten selbst automatisch erweiterbar.In this way, extension components themselves are automatically extensible.

Wo ist MEF verfügbar?Where Is MEF Available?

MEF ist ein wesentlicher Bestandteil des .NET Framework 4.NET Framework 4 und ist überall dort verfügbar, wo .NET Framework verwendet wird.MEF is an integral part of the .NET Framework 4.NET Framework 4, and is available wherever the .NET Framework is used. Sie können MEF in Clientanwendungen verwenden, egal ob bei diesen Windows Forms, WPF oder eine andere Technologie zum Einsatz kommt, oder in Serveranwendungen, die ASP.NET verwenden.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 und MAFMEF and MAF

In früheren Versionen von .NET Framework wurde das Managed Add-in Framework (MAF) eingeführt, das für die Isolation und Verwaltung von Erweiterungen innerhalb von Anwendungen entworfen wurde.Previous versions of the .NET Framework introduced the Managed Add-in Framework (MAF), designed to allow applications to isolate and manage extensions. Bei MAF befindet sich der Fokus auf einer etwas höheren Ebene als bei MEF und liegt auf der Erweiterungsisolation und dem Laden und Entladen von Assemblys, während bei MEF der Fokus auf Erkennbarkeit, Erweiterbarkeit und Portabilität gerichtet ist.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. Die zwei Frameworks arbeiten optimal zusammen, und beide können von Einzelanwendungen verwendet werden.The two frameworks interoperate smoothly, and a single application can take advantage of both.

SimpleCalculator: Eine BeispielanwendungSimpleCalculator: An Example Application

Die einfachste Möglichkeit zur Entdeckung der Möglichkeiten von MEF ist das Erstellen einer einfachen MEF-Anwendung.The simplest way to see what MEF can do is to build a simple MEF application. In diesem Beispiel erstellen Sie einen einfachen Rechner namens SimpleCalculator.In this example, you build a very simple calculator named SimpleCalculator. Das Ziel von SimpleCalculator ist die Erstellung einer Konsolenanwendung, die grundlegende arithmetische Befehle wie "5+3" oder "6-2" verarbeiten kann und korrekte Ergebnisse liefert.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. Mit MEF können mühelos neue Operatoren hinzugefügt werden, ohne dabei den Anwendungscode ändern zu müssen.Using MEF, you'll be able to add new operators without changing the application code.

Der vollständige Code für dieses Beispiel kann unter SimpleCalculator-Beispiel heruntergeladen werden.To download the complete code for this example, see the SimpleCalculator sample.

Hinweis

Mit SimpleCalculator sollen die Konzepte und die Syntax des MEF veranschaulicht werden. Auf ein realistisches Verwendungsszenario wird in diesem Fall kein Wert gelegt.The purpose of SimpleCalculator is to demonstrate the concepts and syntax of MEF, rather than to necessarily provide a realistic scenario for its use. Viele der Anwendungen, die am meisten von der Leistungsfähigkeit von MEF profitieren würden, sind komplexer als SimpleCalculator.Many of the applications that would benefit most from the power of MEF are more complex than SimpleCalculator. Ausführlichere Beispiele finden Sie unter Managed Extensibility Framework auf GitHub.For more extensive examples, see the Managed Extensibility Framework on GitHub.

  • Erstellen Sie zum Einstieg in Visual Studio ein neues Konsolenanwendungsprojekt, und nennen Sie es SimpleCalculator.To start, in Visual Studio, create a new Console Application project and name it SimpleCalculator.

  • Fügen Sie einen Verweis auf die System.ComponentModel.Compositions-Assembly hinzu, in der sich MEF befindet.Add a reference to the System.ComponentModel.Composition assembly, where MEF resides.

  • Öffnen Sie "Module1.vb" oder "Program.cs", und fügen Sie Imports- oder using-Anweisungen für System.ComponentModel.Composition und System.ComponentModel.Composition.Hosting hinzu.Open Module1.vb or Program.cs and add Imports or using statements for System.ComponentModel.Composition and System.ComponentModel.Composition.Hosting. Diese zwei Namespaces enthalten MEF-Typen, die zur Entwicklung einer erweiterbaren Anwendung erforderlich sind.These two namespaces contain MEF types you will need to develop an extensible application.

  • Wenn Sie Visual Basic verwenden, fügen Sie das Public-Schlüsselwort der Zeile hinzu, die das Module1-Modul deklariert.If you're using Visual Basic, add the Public keyword to the line that declares the Module1 module.

Kompositionscontainer und KatalogeComposition Container and Catalogs

Der Kern des MEF-Kompositionsmodells ist der Kompositionscontainer, der alle verfügbaren Teile enthält und die Komposition ausführt.The core of the MEF composition model is the composition container, which contains all the parts available and performs composition. Mit Komposition ist das Zuweisen von Importen zu Exporten gemeint.Composition is the matching up of imports to exports. Der gängigste Kompositionscontainertyp ist CompositionContainer, den wir auch für SimpleCalculator verwenden werden.The most common type of composition container is CompositionContainer, and you'll use this for SimpleCalculator.

Wenn Sie Visual Basic verwenden, fügen Sie in der Datei Module1.vb die öffentliche Klasse Program hinzu.If you're using Visual Basic, in Module1.vb, add a public class named Program.

Fügen Sie der Program-Klasse in "Module1.vb" bzw. "Program.cs" die folgende Zeile hinzu:Add the following line to the Program class in Module1.vb or Program.cs:

Dim _container As CompositionContainer
private CompositionContainer _container;

Zur Ermittlung der verfügbaren Teile verwenden Kompositionscontainern einen Katalog.In order to discover the parts available to it, the composition containers makes use of a catalog. Ein Katalog ist ein Objekt, durch das ermittelte, aus einer beliebigen Quelle stammende Teile verfügbar gemacht werden.A catalog is an object that makes available parts discovered from some source. MEF stellt zur Ermittlung von Teilen in bereitgestellten Typen, Assemblys oder Verzeichnissen Kataloge bereit.MEF provides catalogs to discover parts from a provided type, an assembly, or a directory. Anwendungsentwickler können leicht neue Kataloge zur Ermittlung von Teilen aus anderen Quellen erstellen, z. B. aus einem Webdienst.Application developers can easily create new catalogs to discover parts from other sources, such as a Web service.

Fügen Sie der Program-Klasse den folgenden Konstruktor hinzu: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());
   }
}

Durch den Aufruf von ComposeParts wird der Kompositionscontainer angewiesen, einen bestimmten Satz von Teilen zu verfassen (in diesem Fall die aktuelle Instanz von Program).The call to ComposeParts tells the composition container to compose a specific set of parts, in this case the current instance of Program. Zu diesem Zeitpunkt wird jedoch keine Aktion ausgeführt, da Program über keine Importe zum Füllen verfügt.At this point, however, nothing will happen, since Program has no imports to fill.

Importe und Exporte mit AttributenImports and Exports with Attributes

Importieren Sie zunächst mithilfe von Program einen Rechner.First, you have Program import a calculator. Dies ermöglicht die Trennung von Benutzeroberflächenkomponenten, z. B. der Konsolenein- und -ausgabe, die an Program geleitet werden, von der Logik des Rechners.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.

Fügen Sie der Program-Klasse folgenden Code hinzu:Add the following code to the Program class:

<Import(GetType(ICalculator))>
Public Property calculator As ICalculator
[Import(typeof(ICalculator))]
public ICalculator calculator;

Beachten Sie, dass die Deklaration des calculator-Objekts nicht ungewöhnlich ist, aber durch das ImportAttribute-Attribut ergänzt wird.Notice that the declaration of the calculator object is not unusual, but that it is decorated with the ImportAttribute attribute. Durch das Attribut wird ein Import deklariert, d. h., bei der Komposition des Objekts wird es von der Kompositions-Engine ausgefüllt.This attribute declares something to be an import; that is, it will be filled by the composition engine when the object is composed.

Jeder Import weist einen Vertrag auf, der bestimmt, welche Exporte dem Import zugewiesen werden können.Every import has a contract, which determines what exports it will be matched with. Der Vertrag kann eine explizit angegebene Zeichenfolge sein oder von MEF aus einem angegebenen Typ, in diesem Fall die ICalculator-Schnittstelle, automatisch generiert werden.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. Jeder mit einem entsprechenden Vertrag deklarierte Export kann diesem Import zugewiesen werden.Any export declared with a matching contract will fulfill this import. Der Typ des calculator-Objekts ist zwar tatsächlich ICalculator, allerdings ist dies nicht erforderlich.Note that while the type of the calculator object is in fact ICalculator, this is not required. Der Vertrag ist unabhängig vom Typ des Importobjekts.The contract is independent from the type of the importing object. (In diesem Fall kann typeof(ICalculator) ausgelassen werden.(In this case, you could leave out the typeof(ICalculator). Sofern nicht anders angeben, wird von MEF automatisch angenommen, dass es sich um einen importtypbasierten Vertrag handelt.)MEF will automatically assume the contract to be based on the type of the import unless you specify it explicitly.)

Fügen Sie dem Modul oder dem SimpleCalculator-Namespace diese sehr einfache Schnittstelle hinzu: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);
}

Nach der Definition von ICalculator benötigen wir eine Klasse für die Implementierung.Now that you have defined ICalculator, you need a class that implements it. Fügen Sie dem Modul oder SimpleCalculator-Namespace die folgende Klasse hinzu: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
{

}

Dieser Export entspricht dem Import in Program.Here is the export that will match the import in Program. Damit eine Übereinstimmung zwischen Export und Import vorliegt, muss der Export den gleichen Vertragstyp aufweisen.In order for the export to match the import, the export must have the same contract. Beim Export unter einem Vertrag auf Grundlage von typeof(MySimpleCalculator) wäre ein Konflikt die Folge, und der Import würde nicht ausgefüllt werden. Der Vertrag muss genau übereinstimmen.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.

Da der Kompositionscontainer alle in dieser Assembly verfügbaren Teilen enthält, ist der MySimpleCalculator-Teil verfügbar.Since the composition container will be populated with all the parts available in this assembly, the MySimpleCalculator part will be available. Wenn der Konstruktor für Program eine Komposition für das Program-Objekt ausführt, wird der entsprechende Import mit einem zu diesem Zweck erstellten MySimpleCalculator-Objekt ausgefüllt.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.

Für die Benutzeroberflächenebene (Program) müssen keine weiteren Informationen bereitgestellt werden.The user interface layer (Program) does not need to know anything else. Sie können daher den Rest der Benutzeroberflächenlogik in der Main-Methode ausfüllen.You can therefore fill in the rest of the user interface logic in the Main method.

Fügen Sie der Main-Methode folgenden Code hinzu: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));
    }
}

Durch diesen Code wird lediglich eine Eingabezeile gelesen und die Calculate-Funktion von ICalculator für das Ergebnis aufgerufen, das an die Konsole zurückgegeben wird.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. Mehr Code ist in Program nicht erforderlich.That is all the code you need in Program. Die restlichen Aufgaben werden in den Teilen ausgeführt.All the rest of the work will happen in the parts.

Weitere Importe und ImportManyFurther Imports and ImportMany

Zur Gewährleistung der Erweiterbarkeit von SimpleCalculator muss eine Reihe von Vorgängen importiert werden.In order for SimpleCalculator to be extensible, it needs to import a list of operations. Ein gewöhnliches ImportAttribute-Attribut wird von ausschließlich einem ExportAttribute ausgefüllt.An ordinary ImportAttribute attribute is filled by one and only one ExportAttribute. Sind mehrere Exporte verfügbar, wird von der Kompositions-Engine ein Fehler ausgegeben.If more than one is available, the composition engine produces an error. Zur Erstellung eines Imports, der mit einer beliebigen Anzahl von Exporten ausgefüllt werden kann, verwenden Sie das ImportManyAttribute-Attribut.To create an import that can be filled by any number of exports, you can use the ImportManyAttribute attribute.

Fügen Sie der MySimpleCalculator-Klasse die folgende Eigenschaft für Vorgänge hinzu: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> ist ein von MEF bereitgestellter Typ für indirekte Verweise auf Exporte.Lazy<T,TMetadata> is a type provided by MEF to hold indirect references to exports. Zusätzlich zum exportierten Objekt selbst können hier auch Exportmetadaten oder Informationen abgerufen werden, die das exportierte Objekt beschreiben.Here, in addition to the exported object itself, you also get export metadata, or information that describes the exported object. Jeder Lazy<T,TMetadata> enthält ein IOperation-Objekt, das eine tatsächliche Operation darstellt, und ein IOperationData-Objekt, das die Metadaten darstellt.Each Lazy<T,TMetadata> contains an IOperation object, representing an actual operation, and an IOperationData object, representing its metadata.

Fügen Sie dem Modul oder SimpleCalculator-Namespace die folgenden einfachen Schnittstellen hinzu: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; }
}

In diesem Fall handelt es sich bei den Metadaten für die einzelnen Vorgänge um das Symbol, das diese Operation darstellt, z. B. +, -, * usw.In this case, the metadata for each operation is the symbol that represents that operation, such as +, -, *, and so on. Fügen Sie dem Modul oder SimpleCalculator-Namespace die folgende Klasse hinzu, um die Additionsoperation verfügbar zu machen: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;
    }
}

An der Funktionsweise des ExportAttribute-Attributs hat sich nichts geändert.The ExportAttribute attribute functions as it did before. Das ExportMetadataAttribute-Attribut fügt dem Export Metadaten in Form eines Name-Wert-Paars hinzu.The ExportMetadataAttribute attribute attaches metadata, in the form of a name-value pair, to that export. Add wird zwar von der IOperation-Klasse implementiert; eine Klasse, die IOperationData implementiert, wurde jedoch nicht explizit definiert.While the Add class implements IOperation, a class that implements IOperationData is not explicitly defined. Stattdessen wird eine Klasse, deren Eigenschaften auf den Namen der bereitgestellten Metadaten basieren, implizit von MEF erstellt.Instead, a class is implicitly created by MEF with properties based on the names of the metadata provided. (Dies ist eine von mehreren Möglichkeiten für den Zugriff auf Metadaten in MEF.)(This is one of several ways to access metadata in MEF.)

Die Komposition in MEF ist rekursiv.Composition in MEF is recursive. Sie haben das Program-Objekt, durch das ein ICalculator vom Typ MySimpleCalculator importiert wurde, explizit zusammengesetzt.You explicitly composed the Program object, which imported an ICalculator that turned out to be of type MySimpleCalculator. Von MySimpleCalculator wird wiederum eine Auflistung von IOperation-Objekten importiert. Dieser Import wird zur gleichen Zeit wie die Importe von MySimpleCalculator bei der Erstellung von Program ausgefüllt.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. Würde von der Add-Klasse ein weiterer Import deklariert werden, würde dieser ebenfalls ausgefüllt werden müssen usw.If the Add class declared a further import, that too would have to be filled, and so on. Jeder nicht ausgefüllte Import führt zu einem Fehler bei der Komposition.Any import left unfilled results in a composition error. (Es ist jedoch möglich, Importe als optional zu deklarieren oder ihnen Standardwerte zuzuweisen.)(It is possible, however, to declare imports to be optional or to assign them default values.)

RechnerlogikCalculator Logic

Sind diese Teile bereitgestellt, bleibt nun noch die Rechnerlogik selbst.With these parts in place, all that remains is the calculator logic itself. Fügen Sie der MySimpleCalculator-Klasse den folgenden Code hinzu, um die Calculate-Methode zu implementieren: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!";
}

Durch die anfänglichen Schritte wird die Eingabezeichenfolge analysiert und in linke und rechte Operanden sowie in ein Operatorzeichen eingeteilt.The initial steps parse the input string into left and right operands and an operator character. In der foreach-Schleife wird jeder Member der operations-Auflistung untersucht.In the foreach loop, every member of the operations collection is examined. Diese Objekte sind vom Typ Lazy<T,TMetadata>, und der Zugriff auf die Metadatenwerte und das exportierte Objekt ist mit der Metadata- bzw. Value-Eigenschaft möglich.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. Wird in diesem Fall festgestellt, dass es sich bei der Symbol-Eigenschaft des IOperationData-Objekts um eine Übereinstimmung handelt, ruft der Rechner die Operate-Methode des IOperation-Objekts auf und gibt das Ergebnis zurück.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.

Zur Fertigstellung des Rechners benötigen Sie auch eine Hilfsmethode, die die Position des ersten Zeichens einer Zeichenfolge zurückgibt, bei dem es sich nicht um eine Ziffer handelt.To complete the calculator, you also need a helper method that returns the position of the first non-digit character in a string. Fügen Sie der MySimpleCalculator-Klasse die folgende Hilfsmethode hinzu: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;
}

Sie sollten das Datenbankprojekt jetzt kompilieren und ausführen können.You should now be able to compile and run the project. In Visual Basic müssen Sie sicherstellen, dass Sie Public das Schlüsselwort Module1 hinzugefügt haben.In Visual Basic, make sure that you added the Public keyword to Module1. Geben Sie im Konsolenfenster eine Addition ein, z. B. "5+3", und der Rechner gibt das entsprechende Ergebnis aus.In the console window, type an addition operation, such as "5+3", and the calculator returns the results. Ein beliebiger anderer Operator führt zu einer Meldung, die anzeigt, dass derAny other operator results in the "Operation Not Found!" Vorgang nicht gefunden werden konnte.message.

Erweitern von SimpleCalculator mithilfe einer neuen KlasseExtending SimpleCalculator Using A New Class

Da der Rechner nun funktioniert, kann ganz leicht eine neue Rechenoperation hinzugefügt werden.Now that the calculator works, adding a new operation is easy. Fügen Sie dem Modul oder SimpleCalculator-Namespace die folgende Klasse hinzu: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;
    }
}

Kompilieren Sie das Projekt und führen Sie es aus.Compile and run the project. Geben Sie eine Subtraktion ein, z. B. "5-3".Type a subtraction operation, such as "5-3". Der Rechner unterstützt jetzt Subtraktionen ebenso sowie Additionen.The calculator now supports subtraction as well as addition.

Erweitern von SimpleCalculator mithilfe einer neuen AssemblyExtending SimpleCalculator Using A New Assembly

Das Hinzufügen von Klassen zum Quellcode ist einfach, aber mit MEF kann auch außerhalb des Quellcodes einer Anwendung nach Teilen gesucht werden.Adding classes to the source code is simple enough, but MEF provides the ability to look outside an application’s own source for parts. Zur Veranschaulichung muss SimpleCalculator für die Suche nach Teilen in Verzeichnissen und in seinen eigenen Assemblys durch das Hinzufügen eines DirectoryCatalog angepasst werden.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.

Fügen Sie dem SimpleCalculator-Projekt ein neues Verzeichnis mit dem Namen Extensions hinzu.Add a new directory named Extensions to the SimpleCalculator project. Das Verzeichnis muss auf der Projektebene und nicht auf der Projektmappenebene hinzugefügt werden.Make sure to add it at the project level, and not at the solution level. Fügen Sie der Projektmappe anschließend ein neues ClassLibrary-Projekt mit dem Namen ExtendedOperations hinzu.Then add a new Class Library project to the solution, named ExtendedOperations. Das neue Projekt wird in eine separate Assembly kompiliert.The new project will compile into a separate assembly.

Öffnen Sie den Projekteigenschaften-Designer für das ExtendedOperations-Projekt, und klicken Sie auf die Registerkarte Erstellen oder auf die Registerkarte Kompilieren. Ändern Sie den Buildausgabepfad oder den Ausgabepfad, sodass dieser auf das Erweiterungsverzeichnis im SimpleCalculator-Projektverzeichnis zeigt (..\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\).

Fügen Sie anschließend dem Program-Konstruktor in der Datei Module1.vb oder in der Datei "Program.cs" die folgende Zeile hinzu: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"));

Ersetzen Sie den Beispielpfad durch den Pfad zum Verzeichnis "Erweiterungen".Replace the example path with the path to your Extensions directory. (Dieser absolute Pfad ist nur für Debugzwecke bestimmt.(This absolute path is for debugging purposes only. In einer Produktionsanwendung würde ein relativer Pfad verwendet werden.) Vom DirectoryCatalog werden jetzt alle in den Assemblys im Verzeichnis "Erweiterungen" enthaltenen Teile dem Kompositionscontainer hinzugefügt.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.

Fügen Sie im ExtendedOperations-Projekt Verweise auf SimpleCalculator und System.ComponentModel.Composition hinzu.In the ExtendedOperations project, add references to SimpleCalculator and System.ComponentModel.Composition. Fügen Sie in der ExtendedOperations-Klassendatei eine Imports- oder eine using-Anweisung für System.ComponentModel.Composition hinzu.In the ExtendedOperations class file, add an Imports or a using statement for System.ComponentModel.Composition. Fügen Sie in Visual Basic die Imports-Anweisung für SimpleCalculator hinzu.In Visual Basic, also add an Imports statement for SimpleCalculator. Fügen Sie der ExtendedOperations-Klassendatei anschließend den folgenden Code hinzu: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;
    }
}

Das ExportAttribute-Attribut muss vom gleichen Typ sein wie ImportAttribute, damit der Vertrag als Übereinstimmung betrachtet wird.Note that in order for the contract to match, the ExportAttribute attribute must have the same type as the ImportAttribute.

Kompilieren Sie das Projekt und führen Sie es aus.Compile and run the project. Testen Sie den neuen MOD (%)-Operator.Test the new Mod (%) operator.

SchlussbemerkungConclusion

In diesem Thema wurden die grundlegenden Konzepte des MEF behandelt.This topic covered the basic concepts of MEF.

  • Teile, Kataloge und der KompositionscontainerParts, catalogs, and the composition container

    Die Teile und der Kompositionscontainer sind die Grundbausteine einer MEF-Anwendung.Parts and the composition container are the basic building blocks of a MEF application. Ein Teil ist jedes Objekt, durch das ein Wert importiert oder exportiert wird, einschließlich des Objekts selbst.A part is any object that imports or exports a value, up to and including itself. Ein Katalog stellt eine Auflistung der Teile einer bestimmten Quelle bereit.A catalog provides a collection of parts from a particular source. Der Kompositionscontainer verwendet die von einem Katalog bereitgestellten Teile, um eine Komposition (die Bindung von Importen an Exporte) durchzuführen.The composition container uses the parts provided by a catalog to perform composition, the binding of imports to exports.

  • Importe und ExporteImports and exports

    Durch Importe und Exporte kommunizieren Komponenten miteinander.Imports and exports are the way by which components communicate. Durch einen Import fordert eine Komponente einen bestimmten Wert oder ein Objekt an. Mit einem Export wird die Verfügbarkeit eines Werts angezeigt.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. Jeder Import wird anhand seines Vertrags mit einer Reihe von Exporten verglichen.Each import is matched with a list of exports by way of its contract.

Wo soll ich fortfahren?Where Do I Go Now?

Der vollständige Code für dieses Beispiel kann unter SimpleCalculator-Beispiel heruntergeladen werden.To download the complete code for this example, see the SimpleCalculator sample.

Weitere Informationen und Codebeispiele finden Sie unter Managed Extensibility Framework.For more information and code examples, see Managed Extensibility Framework. Eine Liste der MEF-Typen finden Sie unter dem System.ComponentModel.Composition-Namespace.For a list of the MEF types, see the System.ComponentModel.Composition namespace.