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

Este tópico fornece uma visão geral da Managed Extensibility Framework introduzida no .NET Framework 4.This topic provides an overview of the Managed Extensibility Framework that was introduced in the .NET Framework 4.

O que é a MEF?What is MEF?

A Managed Extensibility Framework ou MEF é uma biblioteca para criar aplicativos leves e extensíveis.The Managed Extensibility Framework or MEF is a library for creating lightweight, extensible applications. Ele permite que os desenvolvedores de aplicativos descubram e usem extensões sem nenhuma configuração necessária.It allows application developers to discover and use extensions with no configuration required. Isso também permite aos desenvolvedores de extensão encapsular o código facilmente e evitar dependências rígidas frágeis.It also lets extension developers easily encapsulate code and avoid fragile hard dependencies. A MEF não só permite que extensões sejam reutilizadas em aplicativos, mas também entre aplicativos.MEF not only allows extensions to be reused within applications, but across applications as well.

O problema de extensibilidadeThe Problem of Extensibility

Imagine que você é arquiteto de um grande aplicativo que deve fornecer suporte para extensibilidade.Imagine that you are the architect of a large application that must provide support for extensibility. Seu aplicativo deve incluir um número possivelmente grande de componentes menores, sendo responsável por sua criação e execução.Your application has to include a potentially large number of smaller components, and is responsible for creating and running them.

O método mais simples para o problema é incluem os componentes como código-fonte no seu aplicativo e chamá-los diretamente do código.The simplest approach to the problem is to include the components as source code in your application, and call them directly from your code. Isso tem várias desvantagens óbvias.This has a number of obvious drawbacks. Mais importante delas, não é possível adicionar novos componentes sem modificar o código-fonte, uma restrição que pode ser aceitável em, por exemplo, um aplicativo Web, mas não funciona em um aplicativo cliente.Most importantly, you cannot add new components without modifying the source code, a restriction that might be acceptable in, for example, a Web application, but is unworkable in a client application. Igualmente problemático, você não terá acesso ao código-fonte dos componentes, pois eles podem ser desenvolvidos por terceiros, pelo mesmo motivo que você não pode permitir que eles acessem o seu.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.

Uma abordagem um pouco mais sofisticada seria fornecer um ponto de extensão ou interface para permitir a separação entre o aplicativo e seus componentes.A slightly more sophisticated approach would be to provide an extension point or interface, to permit decoupling between the application and its components. Com esse modelo, você pode fornecer uma interface que um componente pode implementar e uma API para habilitá-lo a interagir com seu aplicativo.Under this model, you might provide an interface that a component can implement, and an API to enable it to interact with your application. Isso resolve o problema da necessidade de acesso ao código-fonte, mas ainda traz suas próprias dificuldades.This solves the problem of requiring source code access, but it still has its own difficulties.

Como o aplicativo não tem qualquer capacidade para descobrir componentes por conta própria, ele deve ainda ser explicitamente informado de quais componentes estão disponíveis e quais devem ser carregados.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. Normalmente, isso é feito registrando explicitamente os componentes disponíveis em um arquivo de configuração.This is typically accomplished by explicitly registering the available components in a configuration file. Isso significa que garantir que os componentes estão corretos se torna um problema de manutenção, especialmente se for o usuário final e não o desenvolvedor que deverá fazer a atualização.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.

Além disso, os componentes são incapazes de se comunicar entre si, exceto por meio de canais rigidamente definidos do aplicativo em si.In addition, components are incapable of communicating with one another, except through the rigidly defined channels of the application itself. Se o arquiteto do aplicativo não tiver previsto a necessidade de uma comunicação específica, ela geralmente será impossível.If the application architect has not anticipated the need for a particular communication, it is usually impossible.

Por fim, os desenvolvedores de componentes devem aceitar uma dependência forte no assembly que contém a interface implementada.Finally, the component developers must accept a hard dependency on what assembly contains the interface they implement. Isso torna difícil para um componente a ser usado em mais de um aplicativo e também pode criar problemas quando você cria uma estrutura de testes para componentes.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.

O que o MEF ofereceWhat MEF Provides

Em vez desse registro explícito de componentes disponíveis, o MEF oferece uma maneira de descobri-los implicitamente, através de composição.Instead of this explicit registration of available components, MEF provides a way to discover them implicitly, via composition. Um componente do MEF, chamado de peça, especifica declarativamente ambas as suas dependências (conhecidas como importações) e quais recursos (conhecidos como exportações) ele disponibiliza.A MEF component, called a part, declaratively specifies both its dependencies (known as imports) and what capabilities (known as exports) it makes available. Quando uma peça é criada, o mecanismo de composição do MEF atende às suas importações com o que está disponível de outras peças.When a part is created, the MEF composition engine satisfies its imports with what is available from other parts.

Essa abordagem resolve os problemas abordados na seção anterior.This approach solves the problems discussed in the previous section. Como as peças do MEF especificam de forma declarativa seus recursos, elas são descobertas no tempo de execução, o que significa que um aplicativo usar as peças embutidas em código sem referências ou arquivos de configuração frágeis.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. O MEF permite que os aplicativos descubram e examinem as peças por seus metadados, sem instanciá-las ou até mesmo carregar seus assemblies.MEF allows applications to discover and examine parts by their metadata, without instantiating them or even loading their assemblies. Como resultado, não é necessário especificar cuidadosamente quando e como as extensões devem ser carregadas.As a result, there is no need to carefully specify when and how extensions should be loaded.

Além de suas exportações fornecidas, uma peça pode especificar suas importações, será preenchida por outras peças.In addition to its provided exports, a part can specify its imports, which will be filled by other parts. Isso não somente torna a comunicação entre peças possível, mas muito mais fácil, e permite faturamento correto do código.This makes communication among parts not only possible, but easy, and allows for good factoring of code. Por exemplo, serviços comuns a muitos componentes podem ser faturados em uma peça separada e facilmente modificados ou substituídos.For example, services common to many components can be factored into a separate part and easily modified or replaced.

Como o modelo de MEF não exige nenhuma dependência em um assembly de aplicativo específico, ele permite que extensões sejam reusadas de aplicativo para aplicativo.Because the MEF model requires no hard dependency on a particular application assembly, it allows extensions to be reused from application to application. Isso também facilita desenvolver um agente de teste, independente do aplicativo, para testar os componentes de extensão.This also makes it easy to develop a test harness, independent of the application, to test extension components.

Um aplicativo extensível escrito com o MEF declara uma importação que pode ser preenchida por componentes de extensão e também pode declarar exportações para expor serviços de aplicativo para extensões.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. Cada componente de extensão declara uma exportação e também pode declarar importações.Each extension component declares an export, and may also declare imports. Dessa forma, os próprios componentes de extensão são automaticamente extensíveis.In this way, extension components themselves are automatically extensible.

Onde o MEF está disponível?Where Is MEF Available?

O MEF é parte integrante do .NET Framework 4.NET Framework 4 e está disponível em qualquer lugar em que o .NET Framework é usado.MEF is an integral part of the .NET Framework 4.NET Framework 4, and is available wherever the .NET Framework is used. Você pode usar o MEF em seus aplicativos clientes, seja usando o Windows Forms, WPF ou qualquer outra tecnologia ou em aplicativos de servidores que usam o ASP.NET.You can use MEF in your client applications, whether they use Windows Forms, WPF, or any other technology, or in server applications that use ASP.NET.

MEF e MAFMEF and MAF

Versões anteriores do .NET Framework introduziram o MAF (Managed Add-in Framework), projetado para permitir que aplicativos isolem e gerenciem extensões.Previous versions of the .NET Framework introduced the Managed Add-in Framework (MAF), designed to allow applications to isolate and manage extensions. O foco do MAF é em um nível ligeiramente mais elevado que o MEF, concentrando-se no isolamento de extensão e carregamento e descarregamento do assembly, enquanto o foco do MEF é a descoberta, extensibilidade e portabilidade.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. As duas estruturas interoperam sem problemas entre si e um único aplicativo pode aproveitar ambas.The two frameworks interoperate smoothly, and a single application can take advantage of both.

SimpleCalculator: um aplicativo de exemploSimpleCalculator: An Example Application

A maneira mais simples de ver o que o MEF é criar um aplicativo MEF simples.The simplest way to see what MEF can do is to build a simple MEF application. Neste exemplo, você criará uma calculadora muito simples chamada SimpleCalculator.In this example, you build a very simple calculator named SimpleCalculator. A meta da SimpleCalculator é criar um aplicativo de console que aceite comandos aritméticos básicos, no formato "5+3" ou "6-2" e retorne as respostas corretas.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. Usando MEF, você poderá adicionar novos operadores sem alterar o código do aplicativo.Using MEF, you'll be able to add new operators without changing the application code.

Para baixar o código completo deste exemplo, confira o exemplo SimpleCalculator.To download the complete code for this example, see the SimpleCalculator sample.

Observação

A finalidade da SimpleCalculator é demonstrar os conceitos e a sintaxe do MEF, em vez de fornecer necessariamente um cenário realista para seu uso.The purpose of SimpleCalculator is to demonstrate the concepts and syntax of MEF, rather than to necessarily provide a realistic scenario for its use. Muitos dos aplicativos que mais se beneficiariam da potência do MEF são mais complexos que a SimpleCalculator.Many of the applications that would benefit most from the power of MEF are more complex than SimpleCalculator. Para obter exemplos mais abrangentes, confira o Managed Extensibility Framework no GitHub.For more extensive examples, see the Managed Extensibility Framework on GitHub.

  • Para começar, no Visual Studio, crie um novo projeto de aplicativo de Console e dê a ele o nome SimpleCalculator.To start, in Visual Studio, create a new Console Application project and name it SimpleCalculator.

  • Adicione uma referência ao assembly System.ComponentModel.Composition, no qual reside o MEF.Add a reference to the System.ComponentModel.Composition assembly, where MEF resides.

  • Abra o Module1.vb ou o Program.cs e adicione as instruções Imports ou using System.ComponentModel.Composition e System.ComponentModel.Composition.Hosting.Open Module1.vb or Program.cs and add Imports or using statements for System.ComponentModel.Composition and System.ComponentModel.Composition.Hosting. Esses dois namespaces contêm tipos MEF que você precisará para desenvolver um aplicativo extensível.These two namespaces contain MEF types you will need to develop an extensible application.

  • Se você estiver usando o Visual Basic, adicione a palavra-chave Public à linha que declara o módulo Module1.If you're using Visual Basic, add the Public keyword to the line that declares the Module1 module.

Catálogos e contêiner de composiçãoComposition Container and Catalogs

O núcleo do modelo de composição de MEF é o contêiner de composição, que contém todas as peças disponíveis e executa a composição.The core of the MEF composition model is the composition container, which contains all the parts available and performs composition. Composição e a correspondência de importações e exportações.Composition is the matching up of imports to exports. O tipo mais comum de contêiner de composição é CompositionContainer, e você o usará na SimpleCalculator.The most common type of composition container is CompositionContainer, and you'll use this for SimpleCalculator.

Se você estiver usando o Visual Basic, Module1, adicione uma classe pública denominada Program.If you're using Visual Basic, in Module1.vb, add a public class named Program.

Adicione a seguinte linha para à classe Program no Module1 ou Program.cs:Add the following line to the Program class in Module1.vb or Program.cs:

Dim _container As CompositionContainer
private CompositionContainer _container;

Para descobrir as peças disponíveis para ele, os contêineres de composição usam um catálogo.In order to discover the parts available to it, the composition containers makes use of a catalog. Um catálogo é um objeto que disponibiliza as peças descobertas em alguma origem.A catalog is an object that makes available parts discovered from some source. O MEF fornece catálogos para descobrir peças de um tipo fornecido, um assembly ou de um diretório.MEF provides catalogs to discover parts from a provided type, an assembly, or a directory. Os desenvolvedores de aplicativos podem criar facilmente novos catálogos para descobrir as peças de outras fontes, como um serviço Web.Application developers can easily create new catalogs to discover parts from other sources, such as a Web service.

Adicione o seguinte construtor à classe Program:Add the following constructor to the Program class:

Public Sub New()
    'An aggregate catalog that combines multiple catalogs
     Dim catalog = New AggregateCatalog()

    'Adds all the parts found in the same assembly as the Program class
    catalog.Catalogs.Add(New AssemblyCatalog(GetType(Program).Assembly))

    'Create the CompositionContainer with the parts in the catalog
    _container = New CompositionContainer(catalog)

    'Fill the imports of this object
    Try
        _container.ComposeParts(Me)
    Catch ex As Exception
        Console.WriteLine(ex.ToString)
    End Try
End Sub
private Program()
{
    //An aggregate catalog that combines multiple catalogs
    var catalog = new AggregateCatalog();
    //Adds all the parts found in the same assembly as the Program class
    catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));

    //Create the CompositionContainer with the parts in the catalog
    _container = new CompositionContainer(catalog);

    //Fill the imports of this object
    try
    {
        this._container.ComposeParts(this);
    }
    catch (CompositionException compositionException)
    {
        Console.WriteLine(compositionException.ToString());
   }
}

A chamada para ComposeParts instrui o contêiner de composição a compor um conjunto específico de peças, neste caso a instância atual do Program.The call to ComposeParts tells the composition container to compose a specific set of parts, in this case the current instance of Program. Neste ponto, no entanto, nada acontecerá, pois o Program tem não importações a preencher.At this point, however, nothing will happen, since Program has no imports to fill.

Importações e exportações com atributosImports and Exports with Attributes

Primeiro, o Program importa uma calculadora.First, you have Program import a calculator. Isso separa questões de interface do usuário, tal como a entrada e a saída do console que entrará em Program, da lógica da calculadora.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.

Adicione o seguinte código à classe Program:Add the following code to the Program class:

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

Observe que a declaração do objeto calculator não é incomum, mas sim que está decorada com o atributo ImportAttribute.Notice that the declaration of the calculator object is not unusual, but that it is decorated with the ImportAttribute attribute. Esse atributo declara algo como uma importação; ou seja, ele será preenchido pelo mecanismo de composição quando o objeto for composto.This attribute declares something to be an import; that is, it will be filled by the composition engine when the object is composed.

Cada importação tem um contrato, que determina com quais exportações ele corresponderá.Every import has a contract, which determines what exports it will be matched with. O contrato pode ser uma cadeia de caracteres especificada explicitamente ou ele pode ser automaticamente gerado pelo MEF por meio de um determinado tipo, neste caso, a interface ICalculator.The contract can be an explicitly specified string, or it can be automatically generated by MEF from a given type, in this case the interface ICalculator. Qualquer exportação declarada com um contrato correspondente atender a esta importação.Any export declared with a matching contract will fulfill this import. Observe que, enquanto o tipo do objeto calculator é na verdade ICalculator, isso não é necessário.Note that while the type of the calculator object is in fact ICalculator, this is not required. O contrato é independente do tipo do objeto de importação.The contract is independent from the type of the importing object. (Nesse caso, você poderia deixar o typeof(ICalculator).(In this case, you could leave out the typeof(ICalculator). O MEF automaticamente assumirá que o contrato se baseia no tipo da importação, a menos que seja especificado explicitamente.)MEF will automatically assume the contract to be based on the type of the import unless you specify it explicitly.)

Adicione essa interface simples ao módulo ou ao namespace SimpleCalculator:Add this very simple interface to the module or SimpleCalculator namespace:

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

Agora que você definiu o ICalculator, precisará de uma classe que o implemente.Now that you have defined ICalculator, you need a class that implements it. Adicione a seguinte classe ao módulo ou ao namespace SimpleCalculator:Add the following class to the module or SimpleCalculator namespace:

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

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

}

Aqui está a exportação que corresponde à importação em Program.Here is the export that will match the import in Program. Para que a exportação corresponda à importação, a exportação deverá ter o mesmo contrato.In order for the export to match the import, the export must have the same contract. Exportando sob um contrato com base em typeof(MySimpleCalculator) produziria uma incompatibilidade e a importação não seria preenchida. O contrato deve ter correspondência exata.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.

Uma vez que o contêiner de composição será populado com todas as peças disponíveis nesse assembly, a peça MySimpleCalculator estará disponível.Since the composition container will be populated with all the parts available in this assembly, the MySimpleCalculator part will be available. Quando o construtor do Program executa a composição do objeto Program, sua importação será preenchida por um objeto MySimpleCalculator, que será criado para este fim.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.

A camada de interface do usuário (Program) não precisa saber de mais nada.The user interface layer (Program) does not need to know anything else. Portanto, você pode preencher o resto da lógica de interface do usuário no método Main.You can therefore fill in the rest of the user interface logic in the Main method.

Adicione o seguinte código ao método Main:Add the following code to the Main method:

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

Esse código simplesmente lê uma linha de entrada e chama a função Calculate do ICalculator no resultado, que ele grava de volta ao console.This code simply reads a line of input and calls the Calculate function of ICalculator on the result, which it writes back to the console. Este é todo o código necessário no Program.That is all the code you need in Program. Todo o resto do trabalho ocorrerá nas peças.All the rest of the work will happen in the parts.

Outras Importações e ImportManyFurther Imports and ImportMany

Para que a SimpleCalculator seja extensível, ela precisa importar uma lista de operações.In order for SimpleCalculator to be extensible, it needs to import a list of operations. Um atributo ImportAttribute ordinário é preenchido por um e somente um ExportAttribute.An ordinary ImportAttribute attribute is filled by one and only one ExportAttribute. Se houver mais de uma disponível, o mecanismo de composição gerará um erro.If more than one is available, the composition engine produces an error. Para criar uma importação pode ser preenchida por qualquer número de exportações, você pode usar o atributo ImportManyAttribute.To create an import that can be filled by any number of exports, you can use the ImportManyAttribute attribute.

Adicione as seguintes propriedades de operações à classe MySimpleCalculator:Add the following operations property to the MySimpleCalculator class:

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

Lazy<T,TMetadata> é um tipo é fornecido pelo MEF para manter indiretas referências a exportações.Lazy<T,TMetadata> is a type provided by MEF to hold indirect references to exports. Aqui, além do próprio objeto exportado, você também obtém os metadados da exportação ou informações que descrevem o objeto exportado.Here, in addition to the exported object itself, you also get export metadata, or information that describes the exported object. Cada Lazy<T,TMetadata> contém um objeto IOperation, que representa uma operação real, e um objeto IOperationData, que representa seus metadados.Each Lazy<T,TMetadata> contains an IOperation object, representing an actual operation, and an IOperationData object, representing its metadata.

Adicione as interfaces simples a seguir ao módulo ou ao namespace SimpleCalculator:Add the following simple interfaces to the module or SimpleCalculator namespace:

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

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

public interface IOperationData
{
    Char Symbol { get; }
}

Nesse caso, os metadados para cada operação são o símbolo que representa a operação, como +, -, *, e assim por diante.In this case, the metadata for each operation is the symbol that represents that operation, such as +, -, *, and so on. Para disponibilizar a operação de adição, adicione a seguinte classe ao módulo ou ao namespace SimpleCalculator:To make the addition operation available, add the following class to the module or SimpleCalculator namespace:

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

    Public Function Operate(ByVal left As Integer, ByVal right As Integer) As Integer Implements IOperation.Operate
        Return left + right
    End Function
End Class
[Export(typeof(IOperation))]
[ExportMetadata("Symbol", '+')]
class Add: IOperation
{
    public int Operate(int left, int right)
    {
        return left + right;
    }
}

O atributo ExportAttribute funciona como antes.The ExportAttribute attribute functions as it did before. O atributo ExportMetadataAttribute anexa metadados na forma de um par de nome-valor à exportação.The ExportMetadataAttribute attribute attaches metadata, in the form of a name-value pair, to that export. Enquanto a classe Add implementa IOperation, uma classe que implementa IOperationData não é definida explicitamente.While the Add class implements IOperation, a class that implements IOperationData is not explicitly defined. Em vez disso, uma classe é implicitamente criada pelo MEF com propriedades baseadas nos nomes dos metadados fornecidos.Instead, a class is implicitly created by MEF with properties based on the names of the metadata provided. (Isso é uma das várias maneiras de acessar os metadados no MEF.)(This is one of several ways to access metadata in MEF.)

A composição no MEF é recursiva.Composition in MEF is recursive. Você compõe explicitamente o objeto Program, que importou um ICalculator que acabou sendo do tipo MySimpleCalculator.You explicitly composed the Program object, which imported an ICalculator that turned out to be of type MySimpleCalculator. O MySimpleCalculator, por sua vez, importa um conjunto de objetos IOperation e essa importação será preenchida quando MySimpleCalculator é criado, ao mesmo tempo que as importações do Program.MySimpleCalculator, in turn, imports a collection of IOperation objects, and that import will be filled when MySimpleCalculator is created, at the same time as the imports of Program. Se a classe Add declarar outra importação, ela também deverá ser preenchida, e assim por diante.If the Add class declared a further import, that too would have to be filled, and so on. Quaisquer importações não preenchidas resultam em um erro de composição.Any import left unfilled results in a composition error. (É possível, no entanto, declarar importações como opcional ou atribuir valores padrão.)(It is possible, however, to declare imports to be optional or to assign them default values.)

Lógica da CalculadoraCalculator Logic

Com as peças no lugar, falta apenas a própria lógica da calculadora.With these parts in place, all that remains is the calculator logic itself. Adicione o seguinte código na classe MySimpleCalculator para implementar o método Calculate:Add the following code in the MySimpleCalculator class to implement the Calculate method:

Public Function Calculate(ByVal input As String) As String Implements ICalculator.Calculate
    Dim left, right As Integer
    Dim operation As Char
    Dim fn = FindFirstNonDigit(input) 'Finds the operator
    If fn < 0 Then
        Return "Could not parse command."
    End If
    operation = input(fn)
    Try
        left = Integer.Parse(input.Substring(0, fn))
        right = Integer.Parse(input.Substring(fn + 1))
    Catch ex As Exception
        Return "Could not parse command."
    End Try
    For Each i As Lazy(Of IOperation, IOperationData) In operations
        If i.Metadata.symbol = operation Then
            Return i.Value.Operate(left, right).ToString()
        End If
    Next
    Return "Operation not found!"
End Function
public String Calculate(String input)
{
    int left;
    int right;
    Char operation;
    int fn = FindFirstNonDigit(input); //finds the operator
    if (fn < 0) return "Could not parse command.";

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

    operation = input[fn];

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

As etapas iniciais analisam a cadeia de caracteres de entrada em operandos esquerdo e direito e um caractere de operador.The initial steps parse the input string into left and right operands and an operator character. No loop foreach, cada membro da coleção operations é examinado.In the foreach loop, every member of the operations collection is examined. Esses objetos são do tipo Lazy<T,TMetadata> e seus valores de metadados e o objeto exportado o podem ser acessados com as propriedades Metadata e Value, respectivamente.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. Nesse caso, se a propriedade Symbol do objeto IOperationData for correspondente, a calculadora chamará o método Operate do objeto IOperation e retornará o resultado.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.

Para concluir a calculadora, também é necessário um método auxiliar que retorna a posição do primeiro caractere não dígito em uma cadeia de caracteres.To complete the calculator, you also need a helper method that returns the position of the first non-digit character in a string. Adicione o seguinte método auxiliar para a classe MySimpleCalculator:Add the following helper method to the MySimpleCalculator class:

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

Agora você poderá compilar e executar o projeto.You should now be able to compile and run the project. No Visual Basic, certifique-se de adicionar a palavra-chave Public ao Module1.In Visual Basic, make sure that you added the Public keyword to Module1. Na janela do console, digite uma operação de adição, como "5+3", e a calculadora retorna os resultados.In the console window, type an addition operation, such as "5+3", and the calculator returns the results. Qualquer outro operador resulta na mensagem "Operação Não Encontrada!"Any other operator results in the "Operation Not Found!" .message.

Estendendo a SimpleCalculator usando uma nova classeExtending SimpleCalculator Using A New Class

Agora que a calculadora funciona, adicionar uma nova operação é muito fácil.Now that the calculator works, adding a new operation is easy. Adicione a seguinte classe ao módulo ou ao namespace SimpleCalculator:Add the following class to the module or SimpleCalculator namespace:

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

    Public Function Operate(ByVal left As Integer, ByVal right As Integer) As Integer Implements IOperation.Operate
        Return left - right
    End Function
End Class
[Export(typeof(IOperation))]
[ExportMetadata("Symbol", '-')]
class Subtract : IOperation
{
    public int Operate(int left, int right)
    {
        return left - right;
    }
}

Compile e execute o projeto.Compile and run the project. Digite uma operação de subtração, como "5-3".Type a subtraction operation, such as "5-3". A calculadora agora dá suporte à subtração, além da adição.The calculator now supports subtraction as well as addition.

Estendendo a SimpleCalculator usando um novo assemblyExtending SimpleCalculator Using A New Assembly

Adicionar classes ao código-fonte é bastante simples, mas o MEF oferece a capacidade de procurar peças fora de uma fonte do aplicativo.Adding classes to the source code is simple enough, but MEF provides the ability to look outside an application’s own source for parts. Para demonstrar isso, você precisará modificar a SimpleCalculator para pesquisar um diretório, bem como seu próprio assembly, por peças, adicionando um DirectoryCatalog.To demonstrate this, you will need to modify SimpleCalculator to search a directory, as well as its own assembly, for parts, by adding a DirectoryCatalog.

Adicione um novo diretório chamado Extensions ao projeto da SimpleCalculator.Add a new directory named Extensions to the SimpleCalculator project. Certifique-se de adicioná-lo no nível do projeto e não no nível da solução.Make sure to add it at the project level, and not at the solution level. Em seguida, adicione um novo projeto de Biblioteca de Classes à solução, chamado ExtendedOperations.Then add a new Class Library project to the solution, named ExtendedOperations. O novo projeto será compilado em um assembly separado.The new project will compile into a separate assembly.

Abra o Designer de Propriedades de Projeto para o projeto ExtendedOperations e clique na guia Compilar ou Criar. Alterar o Caminho de saída do build ou o Caminho de saída para apontar para o diretório Extensions no diretório do projeto da SimpleCalculator (..\SimpleCalculator\Extensions\).Open the Project Properties Designer for the ExtendedOperations project and click the Compile or Build tab. Change the Build output path or Output path to point to the Extensions directory in the SimpleCalculator project directory (..\SimpleCalculator\Extensions\).

No Module1.vb ou Program.cs, adicione a seguinte linha ao construtor Program:In Module1.vb or Program.cs, add the following line to the Program constructor:

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

Substitua o caminho de exemplo pelo caminho para o diretório de Extensions.Replace the example path with the path to your Extensions directory. (Esse caminho absoluto destina-se apenas a fins de depuração.(This absolute path is for debugging purposes only. Em um aplicativo de produção, você deve usar um caminho relativo.) O DirectoryCatalog agora adicionará as peças encontradas a quaisquer assemblies do diretório Extensions ao contêiner de composição.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.

No projeto ExtendedOperations, adicione referências à SimpleCalculator e ao System.ComponentModel.Composition.In the ExtendedOperations project, add references to SimpleCalculator and System.ComponentModel.Composition. No arquivo de classe ExtendedOperations, adicione um Imports ou uma instrução using para o System.ComponentModel.Composition.In the ExtendedOperations class file, add an Imports or a using statement for System.ComponentModel.Composition. No Visual Basic, adicione também uma instrução Imports para a SimpleCalculator.In Visual Basic, also add an Imports statement for SimpleCalculator. Em seguida, adicione a seguinte classe ao arquivo da classe ExtendedOperations:Then add the following class to the ExtendedOperations class file:

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

    Public Function Operate(ByVal left As Integer, ByVal right As Integer) As Integer Implements IOperation.Operate
        Return left Mod right
    End Function
End Class
[Export(typeof(SimpleCalculator.IOperation))]
[ExportMetadata("Symbol", '%')]
public class Mod : SimpleCalculator.IOperation
{
    public int Operate(int left, int right)
    {
        return left % right;
    }
}

Observe que para o contrato corresponder, o atributo ExportAttribute deve ter o mesmo tipo de ImportAttribute.Note that in order for the contract to match, the ExportAttribute attribute must have the same type as the ImportAttribute.

Compile e execute o projeto.Compile and run the project. Teste o novo operador Mod (%).Test the new Mod (%) operator.

ConclusãoConclusion

Este tópico abordou os conceitos básicos do MEF.This topic covered the basic concepts of MEF.

  • Peças, catálogos e o contêiner de composiçãoParts, catalogs, and the composition container

    Peças e o contêiner de composição são pilares essenciais de um aplicativo MEF.Parts and the composition container are the basic building blocks of a MEF application. Uma peça é qualquer objeto que importa ou exporta um valor, até e incluindo a si mesmo.A part is any object that imports or exports a value, up to and including itself. Um catálogo fornece um conjunto de peças de uma fonte específica.A catalog provides a collection of parts from a particular source. O contêiner de composição usa as peças fornecidas por um catálogo para executar a composição, a associação de importações a exportações.The composition container uses the parts provided by a catalog to perform composition, the binding of imports to exports.

  • Importações e exportaçõesImports and exports

    Importações e exportações são o modo pelo qual os componentes se comunicam.Imports and exports are the way by which components communicate. Com uma importação, o componente especifica uma necessidade por um determinado valor ou objeto e com uma exportação ele especifica a disponibilidade de um valor.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. Cada importação é compatível com uma lista dos exportações por meio do seu contrato.Each import is matched with a list of exports by way of its contract.

Para onde devo ir agora?Where Do I Go Now?

Para baixar o código completo deste exemplo, confira o exemplo SimpleCalculator.To download the complete code for this example, see the SimpleCalculator sample.

Para obter mais informações e exemplos de código, confira Managed Extensibility Framework.For more information and code examples, see Managed Extensibility Framework. Para obter uma lista dos tipos de MEF, confira o namespace System.ComponentModel.Composition.For a list of the MEF types, see the System.ComponentModel.Composition namespace.