Udostępnij za pośrednictwem


Managed Extensibility Framework (MEF)

Ten temat zawiera omówienie Managed Extensibility Framework, które zostały wprowadzone w .NET Framework 4.

Co to jest MEF?

Biblioteka Managed Extensibility Framework lub MEF to biblioteka do tworzenia lekkich i rozszerzalnych aplikacji. Umożliwia ona deweloperom aplikacji odnajdywanie i używanie rozszerzeń bez potrzeby konfigurowania. Umożliwia również deweloperom rozszerzeń łatwe hermetyzowanie kodu i uniknięcie niestabilnych twardych zależności. MeF nie tylko umożliwia ponowne użycie rozszerzeń w aplikacjach, ale również w aplikacjach.

Problem rozszerzalności

Imagine, że jesteś architektem dużej aplikacji, która musi zapewniać obsługę rozszerzalności. Aplikacja musi zawierać potencjalnie dużą liczbę mniejszych składników i jest odpowiedzialna za ich tworzenie i uruchamianie.

Najprostszym podejściem do problemu jest dołącznie składników jako kodu źródłowego w aplikacji i wywołanie ich bezpośrednio z kodu. Ma to wiele oczywistych wad. Co najważniejsze, nie można dodawać nowych składników bez modyfikacji kodu źródłowego, czyli ograniczenia, które może być akceptowalne na przykład w aplikacji internetowej, ale nie jest możliwe w aplikacji klienckiej. Równie problematyczne może być to, że nie masz dostępu do kodu źródłowego składników, ponieważ mogą one być opracowywane przez inne firmy i z tego samego powodu nie możesz zezwolić im na dostęp do Twoich składników.

Nieco bardziej zaawansowane podejście polegałoby na udostępnieniu punktu rozszerzenia lub interfejsu, aby umożliwić rozdzielenie między aplikacją i jej składnikami. W ramach tego modelu możesz udostępnić interfejs, który składnik może zaimplementować, oraz interfejs API umożliwiający interakcję z aplikacją. Rozwiązuje to problem wymagania dostępu do kodu źródłowego, ale nadal ma własne trudności.

Ponieważ aplikacja nie ma żadnej pojemności do odnajdywania składników samodzielnie, nadal musi być jawnie określona, które składniki są dostępne i powinny zostać załadowane. Zwykle jest to realizowane przez jawne zarejestrowanie dostępnych składników w pliku konfiguracji. Oznacza to, że zapewnianie poprawności składników staje się problemem z konserwacją, szczególnie jeśli to użytkownik końcowy, a nie deweloper, który powinien wykonać aktualizację.

Ponadto składniki nie mogą komunikować się ze sobą, z wyjątkiem ściśle zdefiniowanych kanałów samej aplikacji. Jeśli architekt aplikacji nie przewidział potrzeby konkretnej komunikacji, zwykle jest to niemożliwe.

Na koniec deweloperzy składników muszą zaakceptować trudną zależność od tego, jaki zestaw zawiera implementny interfejs. Utrudnia to stosowanie składnika w więcej niż jednej aplikacji, a także może tworzyć problemy podczas tworzenia struktury testowej dla składników.

Co zapewnia mef

Zamiast tej jawnej rejestracji dostępnych składników mef umożliwia ich niejawne odnajdywanie za pośrednictwem kompozycji. Składnik MEF, nazywany częścią, deklaratywnie określa zarówno jego zależności (nazywane importami ), jak i możliwości (nazywane eksportami ), które udostępnia. Po utworzeniu części aparat kompozycji MEF spełnia swoje warunki importu z tym, co jest dostępne z innych części.

To podejście rozwiązuje problemy omówione w poprzedniej sekcji. Ponieważ części MEF deklaratywnie określają swoje możliwości, są wykrywalne w czasie rzeczywistym, co oznacza, że aplikacja może korzystać z części bez zakodowanych odwołań lub niestabilnych plików konfiguracji. MeF umożliwia aplikacjom odnajdywanie i badanie części według ich metadanych bez ich wystąpienia lub nawet ładowania zestawów. W związku z tym nie trzeba dokładnie określać, kiedy i jak mają być ładowane rozszerzenia.

Oprócz dostarczonych eksportów część może określić swoje importy, które zostaną wypełnione przez inne części. Dzięki temu komunikacja między częściami jest nie tylko możliwa, ale i łatwa, a także umożliwia dobre faktorowanie kodu. Na przykład usługi wspólne dla wielu składników mogą być w oddzielnej części i łatwo modyfikowane lub zastępowane.

Ponieważ model MEF nie wymaga stałej zależności od określonego zestawu aplikacji, umożliwia ponowne użycie rozszerzeń z aplikacji do aplikacji. Ułatwia to również opracowywanie aplikacji testowej, niezależnie od aplikacji, do testowania składników rozszerzenia.

Rozszerzalna aplikacja napisana przy użyciu mef deklaruje import, który może zostać wypełniony przez składniki rozszerzenia, a także może deklarować eksporty w celu uwidocznić usługi aplikacji dla rozszerzeń. Każdy składnik rozszerzenia deklaruje eksport i może również deklarować importy. W ten sposób same składniki rozszerzenia są automatycznie rozszerzalne.

Gdzie jest dostępny mef

MEF jest integralną częścią .NET Framework 4 i jest dostępny wszędzie tam, gdzie .NET Framework jest używany. MeF można używać w aplikacjach klienckich, niezależnie od tego, czy używają Windows Forms, WPF, czy innej technologii, albo w aplikacjach serwerowych, które używają ASP.NET.

MEF i MAF

W poprzednich wersjach .NET Framework wprowadzono zarządzaną platformę dodatku (MAF), która umożliwia aplikacjom izolowanie rozszerzeń i zarządzanie nimi. Głównym celem programu MAF jest nieco wyższy poziom niż MEF, który koncentruje się na izolacji rozszerzeń oraz ładowaniu i zwalnianiu zestawu, a mef koncentruje się na odnajdywaniu, rozszerzalności i przenośności. Te dwie struktury bezproblemowo współdziałają, a jedna aplikacja może korzystać z obu tych możliwości.

SimpleCalculator: przykładowa aplikacja

Najprostszym sposobem, aby zobaczyć, co może zrobić mef, jest skompilowanie prostej aplikacji MEF. W tym przykładzie tworzysz bardzo prosty kalkulator o nazwie SimpleCalculator. Celem simpleCalculator jest utworzenie aplikacji konsolowej, która akceptuje podstawowe polecenia arytmetyczne w postaci "5+3" lub "6-2" i zwraca prawidłowe odpowiedzi. Korzystając z funkcji MEF, będziesz mieć możliwość dodawania nowych operatorów bez zmiany kodu aplikacji.

Aby pobrać pełny kod dla tego przykładu, zobacz przykład SimpleCalculator (Visual Basic).

Uwaga

Celem simpleCalculator jest zademonstrowanie koncepcji i składni mef, a nie konieczności zapewnienia realistycznego scenariusza jego użycia. Wiele aplikacji, które najbardziej skorzystają na możliwościach mef, jest bardziej złożonych niż SimpleCalculator. Aby uzyskać bardziej rozbudowane przykłady, zobacz Managed Extensibility Framework na GitHub.

  • Aby rozpocząć, Visual Studio utwórz nowy projekt aplikacja konsolowa i nadaj jej nazwę SimpleCalculator.

  • Dodaj odwołanie do zestawu System.ComponentModel.Composition , w którym znajduje się mef.

  • Otwórz moduł Module1.vb lub Program.cs i dodaj Imports instrukcje lub using dla i System.ComponentModel.Composition.HostingSystem.ComponentModel.Composition . Te dwie przestrzenie nazw zawierają typy MEF potrzebne do opracowania rozszerzalnych aplikacji.

  • Jeśli używasz polecenia Visual Basic, dodaj Public słowo kluczowe do wiersza, który deklaruje Module1 moduł.

Kontener kompozycji i wykazy

Podstawą modelu kompozycji MEF jest kontener kompozycji, który zawiera wszystkie dostępne części i wykonuje kompozycję. Kompozycja to dopasowanie importów do eksportów. Najpopularniejszym typem kontenera kompozycji jest CompositionContainer, a użyjesz go dla simpleCalculator.

Jeśli używasz klasy Visual Basic, dodaj klasę publiczną o nazwie w Program module Module1.vb.

Dodaj następujący wiersz do klasy Program w module1.vb lub Program.cs:

Dim _container As CompositionContainer
private CompositionContainer _container;

Aby odnaleźć dostępne dla niego części, kontenery kompozycji korzystają z katalogu. Wykaz jest obiektem, który udostępnia dostępne części odnalezione z jakiegoś źródła. MeF udostępnia wykazy do odnajdywania części z podanego typu, zestawu lub katalogu. Deweloperzy aplikacji mogą łatwo tworzyć nowe katalogi w celu odnajdywania części z innych źródeł, takich jak usługa internetowa.

Dodaj następujący konstruktor do Program klasy :

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 CompositionException
        Console.WriteLine(ex.ToString)
    End Try
End Sub
private Program()
{
    try
    {
        // 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);
        _container.ComposeParts(this);
    }
    catch (CompositionException compositionException)
    {
        Console.WriteLine(compositionException.ToString());
    }
}

Wywołanie w celu nakazuje ComposeParts kontenerowi kompozycji redagowanie określonego zestawu części, w tym przypadku bieżącego wystąpienia klasy Program. Jednak na tym etapie nic się nie stanie, ponieważ nie Program ma żadnych importów do wypełnienia.

Importy i eksporty z atrybutami

Najpierw zaimportujesz Program kalkulator. Umożliwia to rozdzielenie kwestii dotyczących interfejsu użytkownika, Programtakich jak dane wejściowe i wyjściowe konsoli, które będą trafiać do interfejsu , z logiki kalkulatora.

Dodaj następujący kod do Program klasy :

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

Zwróć uwagę, że deklaracja obiektu nie calculator jest niczym nietypowym, ale jest dekorowany za pomocą atrybutu ImportAttribute . Ten atrybut deklaruje coś jako import; oznacza to, że zostanie on wypełniony przez aparat kompozycji, gdy obiekt jest skomponowany.

Każdy import ma kontrakt, który określa, jakie eksporty zostaną dopasowane. Kontrakt może być jawnie określonym ciągiem lub może być automatycznie generowany przez mef z danego typu, w tym przypadku interfejsu ICalculator. Każdy eksport zadeklarowany za pomocą pasującego kontraktu spowoduje zrealizowanie tego importu. Należy pamiętać, że chociaż typ obiektu calculator jest w rzeczywistości ICalculator, nie jest to wymagane. Kontrakt jest niezależny od typu obiektu importowanego. (W tym przypadku możesz pozostawić . typeof(ICalculator) MeF automatycznie zakłada, że kontrakt jest oparty na typie importu, chyba że zostanie określony jawnie.

Dodaj ten bardzo prosty interfejs do modułu lub przestrzeni nazw SimpleCalculator :

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

Teraz, gdy zdefiniowano klasę ICalculator, potrzebujesz klasy, która ją implementuje. Dodaj następującą klasę do modułu lub przestrzeni SimpleCalculator nazw:

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

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

}

Oto eksport, który będzie odpowiadać importowi w pliku Program. Aby eksport był zgodne z importem, eksport musi mieć ten sam kontrakt. Eksportowanie w ramach kontraktu opartego typeof(MySimpleCalculator) na programie wywłaszłoby niezgodność i import nie zostałby wypełniony; kontrakt musi być dokładnie taki sam.

Ponieważ kontener kompozycji zostanie wypełniony wszystkimi częściami dostępnymi w tym zestawie, MySimpleCalculator część będzie dostępna. Gdy konstruktor dla wykonuje Program kompozycję na Program obiekcie, jego import MySimpleCalculator zostanie wypełniony obiektem, który zostanie utworzony w tym celu.

Warstwa interfejsu użytkownika (Program) nie musi wiedzieć nic innego. W związku z tym możesz wypełnić pozostałą część logiki interfejsu użytkownika w Main metodzie .

Dodaj następujący kod do metody Main:

Sub Main()
    ' Composition is performed in the constructor.
    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)
{
    // Composition is performed in the constructor.
    var p = new Program();
    Console.WriteLine("Enter Command:");
    while (true)
    {
        string s = Console.ReadLine();
        Console.WriteLine(p.calculator.Calculate(s));
    }
}

Ten kod po prostu odczytuje wiersz danych wejściowych i CalculateICalculator wywołuje funkcję w wyniku, którą zapisuje z powrotem do konsoli. To jest cały kod, który jest potrzebny w .Program Cała pozostała część pracy będzie odbywać się w częściach.

Importy i atrybuty ImportMany

Aby simpleCalculator był rozszerzalny, musi zaimportować listę operacji. Zwykły atrybut ImportAttribute jest wypełniany przez jeden i tylko jeden .ExportAttribute Jeśli jest dostępnych więcej niż jeden, aparat kompozycji generuje błąd. Aby utworzyć import, który można wypełnić dowolną liczbą eksportów, można użyć atrybutu ImportManyAttribute .

Dodaj następującą właściwość operations do MySimpleCalculator klasy :

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

Lazy<T,TMetadata> jest typem dostarczonym przez mef do przechowywania pośrednich odwołań do eksportów. W tym miejscu oprócz wyeksportowanego obiektu można również uzyskać metadane eksportu lub informacje opisujące wyeksportowany obiekt. Każdy Lazy<T,TMetadata> obiekt zawiera IOperation obiekt reprezentujący rzeczywistą operację i IOperationData obiekt reprezentujący jego metadane.

Dodaj następujące proste interfejsy do modułu lub przestrzeni nazw SimpleCalculator :

Public Interface IOperation
    Function Operate(left As Integer, 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; }
}

W tym przypadku metadane dla każdej operacji są symbolem reprezentującym tę operację, taką jak +, -, *i tak dalej. Aby udostępnić operację dodawania, dodaj następującą klasę do modułu lub przestrzeni nazw SimpleCalculator :

<Export(GetType(IOperation))>
<ExportMetadata("Symbol", "+"c)>
Public Class Add
    Implements IOperation

    Public Function Operate(left As Integer, 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;
    }
}

Atrybut ExportAttribute działa tak jak wcześniej. Atrybut ExportMetadataAttribute dołącza metadane w postaci pary nazwa-wartość do tego eksportu. Klasa implementuje Add klasę IOperation, która implementuje IOperationData klasę , ale nie jest jawnie zdefiniowana. Zamiast tego klasa jest niejawnie tworzona przez mef z właściwościami opartymi na nazwach podanych metadanych. (Jest to jeden z kilku sposobów uzyskiwania dostępu do metadanych w mef).

Kompozycja w mef jest rekursywna. Jawnie skomponujesz Program obiekt , który zaimportował ICalculator obiekt , który okazał się typem MySimpleCalculator. MySimpleCalculatorz kolei importuje kolekcję IOperation obiektów, MySimpleCalculator a import zostanie wypełniony podczas tworzenia w tym samym czasie co import .Program Jeśli klasa Add zadeklarowała dalsze importowanie, to również musiałoby zostać wypełnione i tak dalej. Każdy import pozostawiony jako niezapełniony powoduje błąd kompozycji. (Można jednak zadeklarować importy jako opcjonalne lub przypisać im wartości domyślne).

Logika kalkulatora

Gdy te części są już dostępne, pozostaje tylko logika kalkulatora. Dodaj następujący kod w klasie MySimpleCalculator , aby zaimplementować Calculate metodę :

Public Function Calculate(input As String) As String Implements ICalculator.Calculate
    Dim left, right As Integer
    Dim operation As Char
    ' Finds the operator.
    Dim fn = FindFirstNonDigit(input)
    If fn < 0 Then
        Return "Could not parse command."
    End If
    operation = input(fn)
    Try
        ' Separate out the operands.
        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;
    // Finds the operator.
    int fn = FindFirstNonDigit(input);
    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!";
}

Początkowe kroki analizują ciąg wejściowy na lewy i prawy operand oraz znak operatora. W pętli foreach sprawdzany jest każdy operations członek kolekcji. Te obiekty są typu Lazy<T,TMetadata>, a ich wartości metadanych MetadataValue i wyeksportowany obiekt są dostępne odpowiednio za pomocą właściwości i . W takim przypadku, jeśli SymbolIOperationData właściwość obiektu zostanie odnaleziona jako dopasowanie, OperateIOperation kalkulator wywoła metodę obiektu i zwróci wynik.

Do ukończenia kalkulatora potrzebna jest również metoda pomocnika, która zwraca pozycję pierwszego znaku niecyfrowego w ciągu. Dodaj następującą metodę pomocnika do MySimpleCalculator klasy :

Private Function FindFirstNonDigit(s As String) As Integer
    For i = 0 To s.Length - 1
        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;
}

Teraz powinno być możliwe skompilowanie i uruchomienie projektu. W Visual Basic upewnij się, że dodano słowo kluczowe Public do .Module1 W oknie konsoli wpisz operację dodatku, taką jak "5+3", a kalkulator zwróci wyniki. Każdy inny operator powoduje komunikat "Nie znaleziono operacji!".

Rozszerzanie klasy SimpleCalculator przy użyciu nowej klasy

Teraz, gdy kalkulator działa, dodawanie nowej operacji jest łatwe. Dodaj następującą klasę do modułu lub przestrzeni SimpleCalculator nazw:

<Export(GetType(IOperation))>
<ExportMetadata("Symbol", "-"c)>
Public Class Subtract
    Implements IOperation

    Public Function Operate(left As Integer, 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;
    }
}

Skompiluj i uruchom projekt. Wpisz operację odejmowania, taką jak "5–3". Kalkulator obsługuje teraz odejmowanie, a także dodawanie.

Rozszerzanie narzędzia SimpleCalculator przy użyciu nowego zestawu

Dodawanie klas do kodu źródłowego jest wystarczająco proste, ale mef zapewnia możliwość wyszukiwania części poza własnym źródłem aplikacji. Aby to zademonstrować, musisz zmodyfikować simpleCalculator, aby przeszukiwać katalog i jego własny zestaw dla części, dodając element DirectoryCatalog.

Dodaj nowy katalog o nazwie Extensions do projektu SimpleCalculator. Pamiętaj, aby dodać go na poziomie projektu, a nie na poziomie rozwiązania. Następnie dodaj do rozwiązania nowy projekt Biblioteka klas o nazwie ExtendedOperations. Nowy projekt zostanie skompilowany w osobnym zestawie.

Otwórz projektanta właściwości Project projektu ExtendedOperations i kliknij kartę Kompiluj lub Kompiluj. Zmień ścieżkę wyjściową kompilacji lub ścieżkę wyjściową, aby wskazać katalog Extensions w katalogu projektu SimpleCalculator (.. \SimpleCalculator\Extensions\).

W module1.vb lub Program.cs dodaj następujący wiersz do konstruktora Program :

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

Zastąp przykładową ścieżkę ścieżką do katalogu Extensions. (Ta ścieżka bezwzględna jest tylko do celów debugowania. W aplikacji produkcyjnej należy użyć ścieżki względnej). Element DirectoryCatalog doda teraz wszystkie części znalezione w dowolnych zestawach w katalogu Extensions do kontenera kompozycji.

W projekcie ExtendedOperations dodaj odwołania do elementów SimpleCalculator i System.ComponentModel.Composition. W pliku klasy ExtendedOperations dodaj Imports instrukcje lub using dla elementu System.ComponentModel.Composition. W Visual Basic dodaj również instrukcje Imports dla simpleCalculator. Następnie dodaj następującą klasę do pliku klasy ExtendedOperations:

<Export(GetType(SimpleCalculator.IOperation))>
<ExportMetadata("Symbol", "%"c)>
Public Class Modulo
    Implements IOperation

    Public Function Operate(left As Integer, 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;
    }
}

Należy pamiętać, że aby kontrakt był taki sam, ExportAttribute atrybut musi mieć ten sam typ co ImportAttribute.

Skompiluj i uruchom projekt. Przetestuj nowy operator Mod (%) .

Podsumowanie

W tym temacie o pojęciach dotyczących mef.

  • Części, wykazy i kontener kompozycji

    Części i kontener kompozycji to podstawowe bloki konstrukcyjne aplikacji MEF. Część to dowolny obiekt, który importuje lub eksportuje wartość, aż do i do samej siebie. Katalog udostępnia kolekcję części z określonego źródła. Kontener kompozycji używa części dostarczonych przez wykaz do wykonania kompozycji, czyli powiązania importów z eksportami.

  • Importy i eksporty

    Importy i eksporty to sposób, w jaki składniki komunikują się. W przypadku importowania składnik określa potrzebę określonej wartości lub obiektu, a po wyeksportowaniu określa dostępność wartości. Każdy import jest do matched z listą eksportów w drodze jego kontraktu.

Następne kroki

Aby pobrać pełny kod dla tego przykładu, zobacz przykład SimpleCalculator (Visual Basic).

Aby uzyskać więcej informacji i przykładów kodu, zobacz Managed Extensibility Framework. Aby uzyskać listę typów MEF, zobacz przestrzeń System.ComponentModel.Composition nazw .