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

En este tema se proporciona información general sobre la biblioteca Managed Extensibility Framework incluida en .NET Framework 4.This topic provides an overview of the Managed Extensibility Framework that was introduced in the .NET Framework 4.

¿Qué es MEF?What is MEF?

Managed Extensibility Framework o MEF es una biblioteca para crear aplicaciones ligeras y extensibles.The Managed Extensibility Framework or MEF is a library for creating lightweight, and extensible applications. Permite a los desarrolladores de aplicaciones detectar y utilizar extensiones sin requisitos de configuración.It allows application developers to discover and use extensions with no configuration required. También permite a los desarrolladores de extensiones encapsular código con facilidad y evitar dependencias lógicas frágiles.It also lets extension developers easily encapsulate code and avoid fragile hard dependencies. MEF no solo permite reutilizar extensiones dentro de las aplicaciones sino también entre aplicaciones.MEF not only allows extensions to be reused within applications, but across applications as well.

El problema de la extensibilidadThe problem of extensibility

Imagine que es el arquitecto de una aplicación grande que debe admitir la extensibilidad.Imagine that you are the architect of a large application that must provide support for extensibility. Su aplicación tiene que incluir un número potencialmente grande de componentes menores, y es responsable de crearlos y ejecutarlos.Your application has to include a potentially large number of smaller components, and is responsible for creating and running them.

El enfoque más sencillo es incluir los componentes como código fuente en su aplicación y llamarlos directamente desde el 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. Esto tiene varias desventajas obvias.This has a number of obvious drawbacks. Mucho más importante es que no puede agregar nuevos componentes sin modificar el código fuente, una restricción que podría ser aceptable en una aplicación web, pero que no es viable en una aplicación 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 es que podría no tener acceso al código fuente de los componentes, porque tal vez haya sido desarrollado por terceros y, por la misma razón, no puede permitirles acceder al suyo.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.

Un enfoque ligeramente más sofisticado sería proporcionar un punto de extensión o interfaz para permitir la desasociación entre la aplicación y sus componentes.A slightly more sophisticated approach would be to provide an extension point or interface, to permit decoupling between the application and its components. Con este modelo, podría proporcionar una interfaz que un componente pueda implementar, y una API para que pueda interactuar con su aplicación.Under this model, you might provide an interface that a component can implement, and an API to enable it to interact with your application. Así se resuelve el problema del acceso al código fuente, pero todavía presenta dificultades.This solves the problem of requiring source code access, but it still has its own difficulties.

Dado que a la aplicación le falta la capacidad para detectar los componentes por sí misma, se debe indicar explícitamente qué componentes están disponibles y deben cargarse.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. Por lo general, esto se logra registrando explícitamente los componentes disponibles en un archivo de configuración.This is typically accomplished by explicitly registering the available components in a configuration file. Esto significa que asegurar que los componentes sean correctos se convierte en una cuestión de mantenimiento, especialmente si usted es el usuario final y no el desarrollador que se espera que haga la actualización.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.

Además, los componentes no pueden comunicarse entre sí, excepto a través de los canales estrictamente definidos en el código de la propia aplicación.In addition, components are incapable of communicating with one another, except through the rigidly defined channels of the application itself. Si el arquitecto de la aplicación no ha previsto la necesidad de una comunicación determinada, normalmente es imposible.If the application architect has not anticipated the need for a particular communication, it is usually impossible.

Por último, los desarrolladores de componentes deben aceptar una dependencia fuerte en el ensamblado que contiene la interfaz que implementan.Finally, the component developers must accept a hard dependency on what assembly contains the interface they implement. Esto dificulta que un componente se use en más de una aplicación y también puede crear problemas cuando se crea un marco de pruebas para los 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.

Qué proporciona MEFWhat MEF provides

En lugar de este registro explícito de componentes disponibles, MEF proporciona una manera de detectarlos implícitamente, mediante la composición.Instead of this explicit registration of available components, MEF provides a way to discover them implicitly, via composition. Un componente MEF, denominado elemento, especifica mediante declaración tanto sus dependencias (conocidas como importaciones) como qué funcionalidades (conocidas como exportaciones) están disponibles.A MEF component, called a part, declaratively specifies both its dependencies (known as imports) and what capabilities (known as exports) it makes available. Cuando se crea un elemento, el motor de composición de MEF cubre sus importaciones con lo que está disponible en otros elementos.When a part is created, the MEF composition engine satisfies its imports with what is available from other parts.

Este enfoque resuelve los problemas comentados en la sección anterior.This approach solves the problems discussed in the previous section. Dado que los elementos MEF especifican sus funcionalidades mediante declaración, son reconocibles en tiempo de ejecución, lo que significa que una aplicación puede utilizar elementos sin referencias incluidas en el código o archivos de configuración frágiles.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. MEF permite a las aplicaciones detectar y examinar elementos por sus metadatos, sin crear instancias ni cargar sus ensamblados.MEF allows applications to discover and examine parts by their metadata, without instantiating them or even loading their assemblies. Por consiguiente, no hay necesidad de especificar meticulosamente cuándo y cómo deben cargarse las extensiones.As a result, there is no need to carefully specify when and how extensions should be loaded.

Además de las exportaciones proporcionadas, un elemento puede especificar las importaciones, que serán completadas por otros elementos.In addition to its provided exports, a part can specify its imports, which will be filled by other parts. Esto hace que la comunicación entre los elementos sea posible además de sencilla y permite una buena factorización del código.This makes communication among parts not only possible, but easy, and allows for good factoring of code. Por ejemplo, los servicios comunes a muchos componentes se pueden tener en cuenta en un elemento independiente, para modificarlos o reemplazarlos con facilidad.For example, services common to many components can be factored into a separate part and easily modified or replaced.

Dado que el modelo MEF no requiere dependencias lógicas en un ensamblado de aplicación determinado, permite reutilizar las extensiones entre una aplicación y otra.Because the MEF model requires no hard dependency on a particular application assembly, it allows extensions to be reused from application to application. Esto también facilita el desarrollo de un agente de prueba, independiente de la aplicación, para probar los componentes de extensión.This also makes it easy to develop a test harness, independent of the application, to test extension components.

Una aplicación extensible escrita con MEF declara una importación que los componentes de extensión pueden completar y también puede declarar exportaciones para exponer servicios de aplicación a extensiones.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 extensión declara una exportación y también puede declarar importaciones.Each extension component declares an export, and may also declare imports. De esta manera, los componentes de extensión son automáticamente extensibles.In this way, extension components themselves are automatically extensible.

Dónde está disponible MEFWhere MEF is available

MEF es una parte integral de .NET Framework 4 y está disponible dondequiera que se use .NET Framework.MEF is an integral part of the .NET Framework 4, and is available wherever the .NET Framework is used. Puede utilizar MEF en aplicaciones cliente, que usen Windows Forms, WPF o cualquier otra tecnología, o en aplicaciones servidor que usen 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 y MAFMEF and MAF

En versiones anteriores de .NET Framework se presentó Managed Add-in Framework (MAF), diseñado para permitir a las aplicaciones aislar y administrar extensiones.Previous versions of the .NET Framework introduced the Managed Add-in Framework (MAF), designed to allow applications to isolate and manage extensions. MAF se centra en un nivel ligeramente más alto que MEF y se concentra en el aislamiento de la extensión y la carga y descarga del ensamblado, mientras que MEF se centra en la detectabilidad, extensibilidad y portabilidad.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. Los dos marcos interoperan fácilmente y una aplicación única puede aprovecharse de ambos.The two frameworks interoperate smoothly, and a single application can take advantage of both.

SimpleCalculator: ejemplo de aplicaciónSimpleCalculator: An example application

La manera más sencilla de ver qué puede hacer MEF es compilar una aplicación sencilla con él.The simplest way to see what MEF can do is to build a simple MEF application. En este ejemplo, compilará una calculadora muy sencilla denominada SimpleCalculator.In this example, you build a very simple calculator named SimpleCalculator. El objetivo de SimpleCalculator es crear una aplicación de consola que acepte comandos aritméticos básicos, con el formato "5+3" ó "6-2", y devuelva respuestas correctas.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. Con MEF, podrá agregar nuevos operadores sin cambiar el código de la aplicación.Using MEF, you'll be able to add new operators without changing the application code.

Para descargar el código completo de este ejemplo, vea Ejemplo SimpleCalculator (Visual Basic).To download the complete code for this example, see the SimpleCalculator sample (Visual Basic).

Nota

La finalidad de SimpleCalculator es mostrar los conceptos y la sintaxis de MEF, más que proporcionar un escenario realista para su 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. Muchas de las aplicaciones que más se beneficiarían de todo lo que MEF puede ofrecer son más complejas que SimpleCalculator.Many of the applications that would benefit most from the power of MEF are more complex than SimpleCalculator. Para obtener ejemplos más exhaustivos, consulte Managed Extensibility Framework en GitHub.For more extensive examples, see the Managed Extensibility Framework on GitHub.

  • Para empezar, en Visual Studio, cree un proyecto de Aplicación de consola y asígnele el nombre SimpleCalculator.To start, in Visual Studio, create a new Console Application project and name it SimpleCalculator.

  • Agregue una referencia al ensamblado System.ComponentModel.Composition, donde se encuentra MEF.Add a reference to the System.ComponentModel.Composition assembly, where MEF resides.

  • Abra Module1.vb o Program.cs, y agregue instrucciones Imports o using para System.ComponentModel.Composition y 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. Estos dos espacios de nombres contienen tipos MEF que necesitará para desarrollar una aplicación extensible.These two namespaces contain MEF types you will need to develop an extensible application.

  • Si usa Visual Basic, agregue la palabra clave Public a la línea que declara el módulo Module1.If you're using Visual Basic, add the Public keyword to the line that declares the Module1 module.

Contenedor de composición y catálogosComposition container and catalogs

El núcleo del modelo de composición de MEF es el contenedor de composición, que contiene todos los elementos disponibles y realiza la composición.The core of the MEF composition model is the composition container, which contains all the parts available and performs composition. La composición es la concordancia de importaciones y exportaciones.Composition is the matching up of imports to exports. El tipo más común de contenedor de composición es CompositionContainer y lo utilizará en SimpleCalculator.The most common type of composition container is CompositionContainer, and you'll use this for SimpleCalculator.

Si usa Visual Basic, agregue una clase pública denominada Program en Module1.vb.If you're using Visual Basic, add a public class named Program in Module1.vb.

Agregue la línea siguiente a la clase Program en Module1.vb o Program.cs:Add the following line to the Program class in Module1.vb or Program.cs:

Dim _container As CompositionContainer
private CompositionContainer _container;

Para detectar los elementos disponibles, el contenedor de composición usa un catálogo.In order to discover the parts available to it, the composition containers makes use of a catalog. Un catálogo es un objeto que hace que los elementos disponibles se detecten en algún origen.A catalog is an object that makes available parts discovered from some source. MEF proporciona catálogos para detectar las partes de un tipo, un ensamblado o un directorio suministrado.MEF provides catalogs to discover parts from a provided type, an assembly, or a directory. Los desarrolladores de aplicaciones pueden crear fácilmente nuevos catálogos para detectar partes de otros orígenes, como un servicio web.Application developers can easily create new catalogs to discover parts from other sources, such as a Web service.

Agregue el siguiente constructor a la clase 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 CompositionException
        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());
   }
}

La llamada a ComposeParts indica al contenedor de composición que cree un conjunto específico de elementos, en este caso la instancia actual de Program.The call to ComposeParts tells the composition container to compose a specific set of parts, in this case the current instance of Program. En este punto, sin embargo, no sucederá nada, dado que Program no tiene importaciones que completar.At this point, however, nothing will happen, since Program has no imports to fill.

Importaciones y exportaciones con atributosImports and Exports with attributes

En primer lugar, haga que Program importe una calculadora.First, you have Program import a calculator. Esto permite la separación de los problemas de la interfaz de usuario, como la entrada y salida de consola que irá a Program, de la lógica de la 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.

Agregue el código siguiente a la clase 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 la declaración del objeto calculator no es inusual, pero se decora con el atributo ImportAttribute.Notice that the declaration of the calculator object is not unusual, but that it is decorated with the ImportAttribute attribute. Este atributo declara que algo es una importación, es decir, el motor de la composición la completará cuando se cree el objeto.This attribute declares something to be an import; that is, it will be filled by the composition engine when the object is composed.

Cada importación tiene un contrato que determina con qué exportaciones coincidirá.Every import has a contract, which determines what exports it will be matched with. El contrato puede ser una cadena especificada explícitamente o MEF puede generarla automáticamente a partir de un tipo determinado, en este caso la interfaz 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. Cualquier exportación declarada con un contrato coincidente completará esta importación.Any export declared with a matching contract will fulfill this import. Tenga en cuenta que mientras el tipo del objeto calculator sea de hecho ICalculator, esto no es necesario.Note that while the type of the calculator object is in fact ICalculator, this is not required. El contrato es independiente del tipo del objeto de importación.The contract is independent from the type of the importing object. (En este caso, podría omitir typeof(ICalculator).(In this case, you could leave out the typeof(ICalculator). MEF adoptará automáticamente el contrato que se va a basar en el tipo de importación, a menos que lo especifique explícitamente).MEF will automatically assume the contract to be based on the type of the import unless you specify it explicitly.)

Agregue esta sencilla interfaz al módulo o al espacio de nombres SimpleCalculator:Add this very simple interface to the module or SimpleCalculator namespace:

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

Ahora que ha definido ICalculator, necesita una clase que lo implemente.Now that you have defined ICalculator, you need a class that implements it. Agregue la siguiente clase al módulo o al espacio de nombres 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
{

}

Aquí está la exportación que coincidirá con la importación en Program.Here is the export that will match the import in Program. Para que la exportación coincida con la importación, la exportación debe tener el mismo contrato.In order for the export to match the import, the export must have the same contract. La exportación sujeta a un contrato basado en typeof(MySimpleCalculator) generará una desigualdad y no se completará la importación; el contrato debe coincidir exactamente.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.

Puesto que el contenedor de composición se rellenará con todos los elementos disponibles de este ensamblado, el elemento MySimpleCalculator estará disponible.Since the composition container will be populated with all the parts available in this assembly, the MySimpleCalculator part will be available. Cuando el constructor Program realiza la composición en el objeto Program, su importación se completará con un objeto MySimpleCalculator, que se creará con ese fin.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.

La capa de interfaz de usuario (Program) no necesita más información.The user interface layer (Program) does not need to know anything else. Por tanto, puede rellenar el resto de la lógica de la interfaz de usuario en el método Main.You can therefore fill in the rest of the user interface logic in the Main method.

Agregue el código siguiente al método Main:Add the following code to the Main method:

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();
    string s;
    Console.WriteLine("Enter Command:");
    while (true)
    {
        s = Console.ReadLine();
        Console.WriteLine(p.calculator.Calculate(s));
    }
}

Este código simplemente lee una línea de entrada y llama a la función Calculate de ICalculator en el resultado, que lo vuelve a escribir en la consola.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. Ese es todo el código que necesita en Program.That is all the code you need in Program. El resto del trabajo se hará en los elementos.All the rest of the work will happen in the parts.

Más importaciones e ImportManyFurther Imports and ImportMany

Para que SimpleCalculator sea extensible, necesita importar una lista de operaciones.In order for SimpleCalculator to be extensible, it needs to import a list of operations. Un atributo ImportAttribute ordinario lo completa un atributo ExportAttribute y solo uno.An ordinary ImportAttribute attribute is filled by one and only one ExportAttribute. Si hay más de uno disponible, el motor de la composición genera un error.If more than one is available, the composition engine produces an error. Para crear una importación que pueda ser completada por cualquier número de exportaciones, puede usar el atributo ImportManyAttribute.To create an import that can be filled by any number of exports, you can use the ImportManyAttribute attribute.

Agregue la siguiente propiedad de operaciones a la clase 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> es un tipo de colección proporcionado por MEF que contiene referencias indirectas a las exportaciones.Lazy<T,TMetadata> is a type provided by MEF to hold indirect references to exports. Aquí, además del propio objeto exportado, también obtiene los metadatos de la exportación o información que describe el objeto exportado.Here, in addition to the exported object itself, you also get export metadata, or information that describes the exported object. Cada colección Lazy<T,TMetadata> contiene un objeto IOperation, que representa una operación real, y un objeto IOperationData, que representa sus metadatos.Each Lazy<T,TMetadata> contains an IOperation object, representing an actual operation, and an IOperationData object, representing its metadata.

Agregue estas sencillas interfaces al módulo o al espacio de nombres SimpleCalculator:Add the following simple interfaces to the module or SimpleCalculator namespace:

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; }
}

En este caso, los metadatos de cada operación son el símbolo que representa esa operación, como +, -, *, etc.In this case, the metadata for each operation is the symbol that represents that operation, such as +, -, *, and so on. Para que la operación de suma esté disponible, agregue la siguiente clase al módulo o al espacio de nombres 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(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;
    }
}

El atributo ExportAttribute funciona igual que antes.The ExportAttribute attribute functions as it did before. El atributo ExportMetadataAttribute adjunta metadatos, con formato de un par nombre-valor, a esa exportación.The ExportMetadataAttribute attribute attaches metadata, in the form of a name-value pair, to that export. Mientras que la clase Add implementa IOperation, no se define explícitamente una clase que implementa IOperationData.While the Add class implements IOperation, a class that implements IOperationData is not explicitly defined. En su lugar, MEF crea implícitamente una clase con propiedades basadas en los nombres de los metadatos proporcionados.Instead, a class is implicitly created by MEF with properties based on the names of the metadata provided. (Esta es una de las maneras de acceder a los metadatos en MEF).(This is one of several ways to access metadata in MEF.)

La composición en MEF es recursiva.Composition in MEF is recursive. Compuso el objeto Program explícitamente, que importó un ICalculator que resultó ser del tipo MySimpleCalculator.You explicitly composed the Program object, which imported an ICalculator that turned out to be of type MySimpleCalculator. A su vez, MySimpleCalculator importa una colección de objetos IOperation y se completará cuando se cree MySimpleCalculator, al mismo tiempo que las importaciones de 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. Si la clase Add declarara una importación más extensa, también tendría que completarse, y así sucesivamente.If the Add class declared a further import, that too would have to be filled, and so on. Toda importación que quede incompleta dará error en la composición.Any import left unfilled results in a composition error. (Es posible, sin embargo, declarar las importaciones para que sean opcionales o asignarles valores predeterminados).(It is possible, however, to declare imports to be optional or to assign them default values.)

Lógica de calculadoraCalculator Logic

Con estos componentes bien establecidos, todo lo que queda es la propia lógica de la calculadora.With these parts in place, all that remains is the calculator logic itself. Agregue el código siguiente a la clase MySimpleCalculator para implementar el método Calculate:Add the following code in the MySimpleCalculator class to implement the Calculate method:

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!";
}

Los pasos iniciales analizan la cadena de entrada en los operandos izquierdo y derecho, y un carácter de operador.The initial steps parse the input string into left and right operands and an operator character. En el bucle de foreach, se examina cada miembro de la colección operations.In the foreach loop, every member of the operations collection is examined. Estos objetos son de tipo Lazy<T,TMetadata>; se puede acceder a los valores de sus metadatos y al objeto exportado con las propiedades Metadata y 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. En este caso, si la propiedad Symbol del objeto IOperationData se detecta como una coincidencia, la calculadora llama al método Operate del objeto IOperation y devuelve el 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 completar la calculadora, también necesita un método del asistente que devuelve la posición del primer carácter de una cadena que no sea un dígito.To complete the calculator, you also need a helper method that returns the position of the first non-digit character in a string. Agregue el método del asistente siguiente a la clase MySimpleCalculator:Add the following helper method to the MySimpleCalculator class:

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;
}

A partir de este momento, podrá compilar y ejecutar el proyecto.You should now be able to compile and run the project. En Visual Basic, asegúrese de que ha agregado la palabra clave Public a Module1.In Visual Basic, make sure that you added the Public keyword to Module1. En la ventana de consola, escriba una operación de suma, como "5+3", y la calculadora devolverá los resultados.In the console window, type an addition operation, such as "5+3", and the calculator returns the results. Cualquier otro operador dará como resultado el mensaje "OperaciónAny other operator results in the "Operation Not Found!" no encontrada".message.

Extensión de SimpleCalculator mediante una clase nuevaExtending SimpleCalculator using a new class

Ahora que la calculadora funciona, agregar una nueva operación es fácil.Now that the calculator works, adding a new operation is easy. Agregue la siguiente clase al módulo o al espacio de nombres 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(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;
    }
}

Compile y ejecute el proyecto.Compile and run the project. Escriba una operación de resta, como "5-3".Type a subtraction operation, such as "5-3". La calculadora admite ahora la resta además de la suma.The calculator now supports subtraction as well as addition.

Extensión de SimpleCalculator con un ensamblado nuevoExtending SimpleCalculator using a new assembly

Agregar clases al código fuente es bastante sencillo, pero MEF permite buscar elementos fuera del propio origen de una aplicación.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 demostrar esto, necesitará modificar SimpleCalculator para que busque los elementos en un directorio y en su propio ensamblado agregando 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.

Agregue un directorio denominado Extensions al proyecto SimpleCalculator.Add a new directory named Extensions to the SimpleCalculator project. Asegúrese de agregarlo en el nivel del proyecto y no en el nivel de la solución.Make sure to add it at the project level, and not at the solution level. Después agregue a la solución un proyecto de biblioteca de clases denominado ExtendedOperations.Then add a new Class Library project to the solution, named ExtendedOperations. El nuevo proyecto se compilará en un ensamblado independiente.The new project will compile into a separate assembly.

Abra el diseñador de propiedades de proyectos para el proyecto ExtendedOperations y haga clic en la pestaña Compilar o Compilación. Cambie Ruta de acceso de los resultados de la compilación o Ruta de acceso de salida para que apunte al directorio Extensions del directorio del proyecto 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\).

En Module1.vb o Program.cs, agregue la línea siguiente al constructor de 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"));

Reemplace la ruta de acceso del ejemplo por la ruta de acceso del directorio Extensions.Replace the example path with the path to your Extensions directory. (Esta ruta de acceso absoluta es para fines de depuración solamente.(This absolute path is for debugging purposes only. En una aplicación de producción, usaría una ruta de acceso relativa). DirectoryCatalog ahora agregará los elementos encontrados en cualquier ensamblado del directorio Extensions al contenedor de composición.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.

En el proyecto ExtendedOperations, agregue referencias a SimpleCalculator y System.ComponentModel.Composition.In the ExtendedOperations project, add references to SimpleCalculator and System.ComponentModel.Composition. En el archivo de clase ExtendedOperations, agregue una instrucción Imports o using para System.ComponentModel.Composition.In the ExtendedOperations class file, add an Imports or a using statement for System.ComponentModel.Composition. En Visual Basic, agregue también una instrucción Imports para SimpleCalculator.In Visual Basic, also add an Imports statement for SimpleCalculator. Después, agregue la clase siguiente al archivo de clase 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(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;
    }
}

Tenga en cuenta que para que el contrato coincida, el atributo ExportAttribute debe tener el mismo tipo que ImportAttribute.Note that in order for the contract to match, the ExportAttribute attribute must have the same type as the ImportAttribute.

Compile y ejecute el proyecto.Compile and run the project. Pruebe el nuevo operador Mod (%).Test the new Mod (%) operator.

ConclusiónConclusion

En este tema se han tratado los conceptos básicos de MEF.This topic covered the basic concepts of MEF.

  • Elementos, catálogos y el contenedor de composiciónParts, catalogs, and the composition container

    Los elementos y el contenedor de composición son los pilares fundamentales de una aplicación MEF.Parts and the composition container are the basic building blocks of a MEF application. Un elemento es cualquier objeto que importa o exporta un valor, hasta sí mismo incluido.A part is any object that imports or exports a value, up to and including itself. Un catálogo proporciona una colección de elementos de un origen determinado.A catalog provides a collection of parts from a particular source. El contenedor de composición utiliza los elementos proporcionados por un catálogo para realizar la composición, el enlace de las importaciones a las exportaciones.The composition container uses the parts provided by a catalog to perform composition, the binding of imports to exports.

  • Importaciones y exportacionesImports and exports

    Las importaciones y las exportaciones son la manera en la que los componentes se comunican.Imports and exports are the way by which components communicate. Con una importación, el componente especifica la necesidad de un valor u objeto determinado, y con una exportación se especifica la disponibilidad de un 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 importación coincide con una lista de exportaciones por medio de su contrato.Each import is matched with a list of exports by way of its contract.

Pasos siguientesNext steps

Para descargar el código completo de este ejemplo, vea Ejemplo SimpleCalculator (Visual Basic).To download the complete code for this example, see the SimpleCalculator sample (Visual Basic).

Para más información y ejemplos de código, vea Managed Extensibility Framework.For more information and code examples, see Managed Extensibility Framework. Para obtener una lista de los tipos de MEF, vea el espacio de nombres System.ComponentModel.Composition.For a list of the MEF types, see the System.ComponentModel.Composition namespace.