Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
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
SimpleCalculator
nazwę .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
lubusing
dlaSystem.ComponentModel.Composition
iSystem.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 deklarujeModule1
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 ICalculator
element , 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.