Udostępnij za pośrednictwem


Managed Extensibility Framework (MEF) – Zarządzany Framework Rozszerzeń

Ten artykuł zawiera omówienie programu Managed Extensibility Framework wprowadzonego w programie .NET Framework 4.

Co to jest MEF?

Managed Extensibility Framework (MEF) to biblioteka umożliwiająca tworzenie uproszczonych i rozszerzalnych aplikacji. Umożliwia deweloperom aplikacji odnajdywanie rozszerzeń i korzystanie z nich bez konieczności konfigurowania. Umożliwia również deweloperom rozszerzeń łatwe hermetyzowanie kodu i unikanie kruchych twardych zależności. MEF umożliwia nie tylko ponowne używanie rozszerzeń w aplikacjach, ale także pomiędzy aplikacjami.

Problem rozszerzalności

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

Najprostszym podejściem do problemu jest uwzględnienie składników jako kodu źródłowego w aplikacji i wywołanie ich bezpośrednio z kodu. Ma to szereg oczywistych wad. Co najważniejsze, nie można dodawać nowych składników bez modyfikowania kodu źródłowego, ograniczenie, które może być dopuszczalne, na przykład w aplikacji internetowej, ale nie jest możliwe w aplikacji klienckiej. Równie problematyczne może okazać się, ż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żna zezwolić im na dostęp do Twoich.

Nieco bardziej wyrafinowane podejście to zapewnienie punktu lub interfejsu rozszerzenia w celu umożliwienia oddzielenia między aplikacją a 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 wymagający 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 poinformowana, które składniki są dostępne i powinny być ładowane. Jest to zwykle realizowane przez jawne zarejestrowanie dostępnych składników w pliku konfiguracji. Oznacza to, że zapewnienie, że składniki są poprawne, staje się problemem z konserwacją, szczególnie jeśli jest to użytkownik końcowy, a nie deweloper, który ma wykonać aktualizację.

Ponadto składniki nie są w stanie komunikować się ze sobą, z wyjątkiem sztywno zdefiniowanych kanałów samej aplikacji. Jeśli architekt aplikacji nie spodziewał się potrzeby określonej komunikacji, zwykle jest to niemożliwe.

Na koniec deweloperzy składników muszą zaakceptować twardą zależność od zestawu, który zawiera interfejs, który implementują. Utrudnia to stosowanie składnika w więcej niż jednej aplikacji i może również powodować problemy podczas tworzenia struktury testowej dla składników.

Co zapewnia mef

Zamiast jawnej rejestracji dostępnych składników, mef zapewnia sposób odnajdywania ich niejawnie za pomocą kompozycji. Składnik MEF, nazywany częścią, deklaratywnie określa zarówno jego zależności (znane jako importy) i możliwości (znane jako eksporty), które udostępnia. Po utworzeniu części aparat kompozycji MEF zaspokaja swoje importy korzystając z dostępnych zasobów 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ą one wykrywalne w czasie wykonywania, co oznacza, że aplikacja może korzystać z części bez zakodowanych odwołań lub kruchych plików konfiguracji. MEF umożliwia aplikacjom odkrywanie i badanie części na podstawie ich metadanych, bez konieczności tworzenia wystąpień tych części lub nawet ładowania ich 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 nie tylko jest możliwa, ale również łatwa, co pozwala na dobrą refaktoryzację kodu. Na przykład usługi wspólne dla wielu składników mogą być uwzględniane w oddzielnej części i łatwo modyfikować lub zastępować.

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

Rozszerzalna aplikacja napisana przy użyciu MEF deklaruje import, który może być wypełniany przez składniki rozszerzeń, a także może deklarować eksporty, aby udostępniać usługi aplikacji rozszerzeniom. Każdy składnik rozszerzenia deklaruje eksport, a także może zadeklarować importy. W ten sposób składniki rozszerzeń są automatycznie rozszerzalne.

Gdzie mef jest dostępny

MEF jest integralną częścią programu .NET Framework 4 i jest dostępny wszędzie tam, gdzie jest używany program .NET Framework. Usługi MEF można używać w aplikacjach klienckich, niezależnie od tego, czy korzystają z windows Forms, WPF, czy innej technologii, czy w aplikacjach serwerowych korzystających z ASP.NET.

MEF i MAF

Poprzednie wersje programu .NET Framework wprowadziły program Managed Add-in Framework (MAF), który umożliwia aplikacjom izolowanie rozszerzeń i zarządzanie nimi. MAF koncentruje się na nieco wyższym poziomie niż MEF, skupiając się na izolacji rozszerzeń oraz ładowaniu i zwalnianiu bibliotek, podczas gdy MEF koncentruje się na odnajdywaniu, rozszerzalności i przenośności. Obie struktury współdziałają bezproblemowo, a jedna aplikacja może korzystać z obu tych platform.

SimpleCalculator: przykładowa aplikacja

Najprostszym sposobem sprawdzenia, co może zrobić MEF, jest utworzenie prostej aplikacji MEF. W tym przykładzie utworzysz 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 poprawne odpowiedzi. Korzystając ze środowiska MEF, będziesz mieć możliwość dodawania nowych operatorów bez zmieniania kodu aplikacji.

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

Uwaga / Notatka

Celem simpleCalculator jest zademonstrowanie koncepcji i składni MEF, a nie koniecznie zapewnienie realistycznego scenariusza do jego użycia. Wiele aplikacji, które najbardziej skorzystałyby z możliwości MEF, jest bardziej złożonych niż SimpleCalculator. Aby uzyskać bardziej obszerne przykłady, zobacz Managed Extensibility Framework w witrynie GitHub.

  • Aby rozpocząć, w programie Visual Studio utwórz nowy projekt Aplikacja konsolowa i nadaj mu SimpleCalculatornazwę .

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

  • Otwórz Module1.vb lub Program.cs i dodaj dyrektywy Imports lub using dla System.ComponentModel.Composition i System.ComponentModel.Composition.Hosting. Te dwie przestrzenie nazw zawierają typy MEF, których potrzebujesz, aby opracować rozszerzalną aplikację.

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

Kontener kompozycji i katalogi

Podstawą modelu kompozycji MEF jest kontener kompozycji, który zawiera wszystkie dostępne części i wykonuje kompozycję. Kompozycja to dopasowanie importu do eksportu. Najczęstszym typem kontenera kompozycji jest CompositionContainer, a użyjesz go dla SimpleCalculator.

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

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

Dim _container As CompositionContainer
private CompositionContainer _container;

Aby odnaleźć dostępne części, kontenery kompozycji korzystają z katalogu. Wykaz to obiekt, który udostępnia części odnalezione z jakiegoś źródła. MeF udostępnia wykazy umożliwiające odnajdywanie części z podanego typu, zestawu lub katalogu. Deweloperzy aplikacji mogą łatwo tworzyć nowe wykazy w celu odnajdywania części z innych źródeł, takich jak usługa sieci Web.

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 polecenia ComposeParts nakazuje kontenerowi kompozycji utworzenie określonego zestawu części, w tym przypadku bieżącego wystąpienia klasy Program. W tym momencie jednak nic się nie stanie, ponieważ Program nie ma importu do wypełnienia.

Importy i eksporty z atrybutami

Najpierw zaimportujesz Program kalkulator. Dzięki temu można oddzielić kwestie związane z interfejsem użytkownika, takie jak dane wejściowe i wyjściowe konsoli, które trafią do Program elementu, od 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 calculator obiektu nie jest nietypowa, ale jest ozdobiona atrybutem ImportAttribute . Ten atrybut deklaruje coś, co ma być importem; oznacza to, że zostanie wypełniony przez silnik kompozycji, gdy obiekt jest komponowany.

Każdy import ma kontrakt, który określa, z którymi eksportami będzie zgodny. Kontrakt może być jawnie określonym ciągiem lub może być automatycznie generowany przez MEF na podstawie danego typu, w tym przypadku interfejsu ICalculator. Każdy eksport zadeklarowany zgodnie z odpowiednim kontraktem spełni ten import. Należy pamiętać, że chociaż typ calculator obiektu jest w rzeczywistości ICalculator, nie jest to wymagane. Kontrakt jest niezależny od typu obiektu importu. (W tym przypadku można pominąć wartość typeof(ICalculator). MeF automatycznie zakłada, że kontrakt będzie oparty na typie importu, chyba że określisz go 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 ICalculatorelement , potrzebna jest klasa, która ją implementuje. Do modułu lub przestrzeni nazw SimpleCalculator dodaj następującą klasę:

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

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

}

Oto eksport, który będzie zgodny z importem w pliku Program. Aby eksport był zgodny z importem, eksport musi mieć ten sam kontrakt. Eksportowanie na podstawie umowy opartej na typeof(MySimpleCalculator) spowodowałoby niezgodność, a import nie zostałby wypełniony; umowa musi dokładnie odpowiadać.

Ponieważ kontener kompozycji zostanie wypełniony wszystkimi elementami dostępnymi w tym zestawie, element MySimpleCalculator będzie dostępny. Gdy konstruktor Program wykonuje kompozycję na obiekcie Program, jego miejsce importu zostanie wypełnione obiektem MySimpleCalculator, który zostanie utworzony w tym celu.

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

Dodaj do metody Main następujący kod:

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 wywołuje Calculate funkcję ICalculator wyniku, którą zapisuje z powrotem w konsoli. To jest cały kod potrzebny w pliku Program. Wszystkie pozostałe prace będą wykonywane w częściach.

Atrybuty Import i ImportMany

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

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

<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 odwołań pośrednich do eksportu. W tym miejscu oprócz wyeksportowanego obiektu uzyskasz również metadane eksportu lub informacje opisujące wyeksportowany obiekt. Każdy Lazy<T,TMetadata> zawiera IOperation obiekt reprezentujący rzeczywistą operację i IOperationData obiekt reprezentujący jego metadane.

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

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 takim przypadku metadane dla każdej operacji są symbolem reprezentującym tę operację, taką jak +, -, *, itd. Aby udostępnić operację dodawania, dodaj następującą klasę do modułu lub SimpleCalculator przestrzeni nazw:

<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 name-value do tego eksportu. Podczas gdy klasa Add implementuje IOperation, nie jest jawnie zdefiniowana klasa, która implementuje IOperationData. 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 skomponowałeś obiekt Program, który zaimportował ICalculator, okazujący się być typu MySimpleCalculator. MySimpleCalculator importuje kolekcję obiektów IOperation, a ten import zostanie wypełniony w momencie tworzenia MySimpleCalculator, równocześnie z importami Program. Jeśli klasa Add zadeklarowała kolejny import, także musiałby zostać wypełniony, i tak dalej. Każdy nieuzupełniony import powoduje błąd kompozycji. (Istnieje jednak możliwość zadeklarowania importu jako opcjonalnego lub przypisania ich wartości domyślnych).

Logika kalkulatora

Dzięki tym częściom, wszystko, co pozostaje, to sama logika kalkulatora. Dodaj następujący kod w klasie, MySimpleCalculator aby zaimplementować metodę Calculate :

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 w lewy i prawy operandy oraz znak operatora. W pętli foreach sprawdzany jest każdy element kolekcji operations. Te obiekty są typu Lazy<T,TMetadata>, a do ich wartości metadanych i wyeksportowanego obiektu można uzyskać dostęp poprzez właściwości Metadata i Value odpowiednio. W takim przypadku, jeśli Symbol właściwość IOperationData obiektu zostanie odnaleziona jako zgodna, kalkulator wywołuje Operate metodę IOperation obiektu i zwraca wynik.

Aby ukończyć kalkulator, potrzebna jest również metoda pomocnicza zwracająca pozycję pierwszego znaku innego niż cyfra w ciągu znaków. 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 języku Visual Basic upewnij się, że dodano Public słowo kluczowe do Module1. W oknie konsoli wpisz operację dodawania, taką jak "5+3", a kalkulator zwraca wyniki. Każdy inny operator powoduje wyświetlenie komunikatu "Nie znaleziono operacji".

Rozszerzanie funkcji SimpleCalculator przy użyciu nowej klasy

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

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

Rozszerz SimpleCalculator korzystając z nowego zestawu

Dodawanie klas do kodu źródłowego jest wystarczająco proste, ale MEF zapewnia możliwość przeszukiwania poza własnym kodem aplikacji w poszukiwaniu komponentów. Aby to zademonstrować, należy zmodyfikować SimpleCalculator, aby wyszukiwał katalog oraz własny zestaw w poszukiwaniu 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 o nazwie ExtendedOperations, będący biblioteką klas. Nowy projekt zostanie skompilowany w osobnym zestawie.

Otwórz Projektanta Właściwości Projektu dla projektu ExtendedOperations i kliknij kartę Kompiluj lub Kompilacja. Zmień ścieżkę docelową kompilacji lub ścieżkę docelową, aby wskazywała 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 służy tylko do debugowania. W aplikacji produkcyjnej należy użyć ścieżki względnej.) Teraz DirectoryCatalog doda wszystkie części znajdujące się w dowolnych zestawach w katalogu Extensions do pojemnika kompozycji.

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

<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ł zgodny, ExportAttribute atrybut musi mieć taki sam typ jak ImportAttribute.

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

Podsumowanie

W tym temacie omówiono podstawowe pojęcia dotyczące MEF.

  • Części, katalogi i kontener kompozycji

    Części i kontener kompozycji to podstawowe bloki konstrukcyjne aplikacji MEF. Częścią jest dowolny obiekt, który importuje lub eksportuje wartość, włącznie z samym sobą. Wykaz zawiera kolekcję części z określonego źródła. Kontener kompozycji używa części dostarczonych przez katalog do wykonywania kompozycji, łączenia importu z eksportem.

  • Importy i eksporty

    Importy i eksporty są sposobem, w jaki składniki komunikują się. Podczas importowania składnik określa potrzebę określonej wartości lub obiektu, a eksport określa dostępność wartości. Każdy import jest dopasowany do listy eksportów poprzez jego kontrakt.

Dalsze 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 (Struktura rozszerzalności zarządzanej). Aby uzyskać listę typów MEF, zobacz System.ComponentModel.Composition przestrzeń nazw.