다음을 통해 공유


Managed Extensibility Framework 개요

업데이트: 2010년 7월

이 항목에서는 .NET Framework 4에 도입된 Managed Extensibility Framework를 소개합니다.

이 항목에는 다음 단원이 포함되어 있습니다.

  • MEF 소개
  • 확장성 문제
  • MEF에서 제공하는 기능
  • MEF를 사용할 수 있는 경우
  • MEF 및 MAF
  • SimpleCalculator: 예제 응용 프로그램
  • 컴퍼지션 컨테이너 및 카탈로그
  • 특성이 지정된 가져오기 및 내보내기
  • 추가 가져오기 및 ImportMany
  • 계산기 논리
  • 새 클래스를 사용하여 SimpleCalculator 확장
  • 새 어셈블리를 사용하여 SimpleCalculator 확장
  • 결론
  • 추가 정보

MEF 소개

Managed Extensibility Framework 또는 MEF는 확장 가능한 경량 응용 프로그램을 만드는 데 사용할 수 있는 라이브러리입니다. 응용 프로그램 개발자는 MEF를 사용하여 별도의 구성 없이 확장을 검색하고 사용할 수 있습니다. 또한 확장 개발자는 MEF를 통해 코드를 쉽게 캡슐화하고 강한 종속성으로 인한 취약성을 방지할 수 있습니다. MEF는 확장을 응용 프로그램 내에서 재사용하는 것은 물론 응용 프로그램 간에도 재사용할 수 있도록 합니다.

확장성 문제

확장성을 지원해야 하는 대규모 응용 프로그램을 설계하는 경우를 가정해 봅니다. 이 경우 응용 프로그램은 작은 구성 요소를 다수 포함해야 하며 이러한 구성 요소를 만들고 실행하는 역할을 맡아야 합니다.

이 문제를 해결하는 가장 간단한 방법은 구성 요소를 응용 프로그램에 소스 코드로 포함하고 구성 요소를 코드에서 직접 호출하는 것입니다. 그러나 이 방법에는 여러 가지 명백한 단점이 있습니다. 가장 중요한 단점은 소스 코드 수정 없이는 새 구성 요소를 추가할 수 없다는 것입니다. 이러한 제한은 웹 응용 프로그램에서는 문제가 되지 않을 수 있지만 클라이언트 응용 프로그램의 경우에는 사정이 다릅니다. 또한 구성 요소가 타사에서 개발될 수 있기 때문에 구성 요소의 소스 코드에 액세스하지 못하는 문제가 발생할 수 있으며, 반대로 내부에서 만든 구성 요소의 소스 코드에 타사 개발자가 액세스하지 못하는 문제도 발생합니다.

이 방법보다 정교한 방법은 확장 지점 또는 인터페이스를 제공하여 응용 프로그램과 구성 요소 간 분리를 허용하는 것입니다. 이 모델에서는 구성 요소가 구현할 수 있는 인터페이스 및 응용 프로그램과의 상호 작용을 가능하게 하는 API를 제공할 수 있습니다. 이 방법의 경우 소스 코드에 액세스해야 하는 문제는 해결되지만 이 방법에도 고유한 문제점이 있습니다.

응용 프로그램에는 구성 요소를 자체적으로 검색할 수 있는 기능이 없기 때문에 사용 가능한 구성 요소 및 로드해야 할 구성 요소를 응용 프로그램에 명시적으로 알려 주어야 합니다. 일반적으로 이 문제는 사용 가능한 구성 요소를 구성 파일에 명시적으로 등록하는 방법으로 해결됩니다. 따라서 구성 요소를 올바르게 하는 것은 유지 관리 차원의 문제이며, 특히 업데이트를 담당하는 사람이 개발자가 아니라 최종 사용자인 경우에는 더욱 그렇습니다.

또한 구성 요소는 응용 프로그램 자체에 엄격하게 정의된 채널을 통하지 않으면 서로 통신할 수 없습니다. 응용 프로그램 설계자가 특정 통신의 필요성을 고려하지 않은 경우에는 일반적으로 구성 요소 간 통신이 불가능합니다.

끝으로, 구성 요소 개발자는 구현할 인터페이스를 포함하는 어셈블리에 대한 강한 종속성을 감수해야 합니다. 이로 인해 구성 요소를 둘 이상의 응용 프로그램에서 사용하기 어려우며, 구성 요소를 위한 테스트 프레임워크를 만들 경우에도 문제가 발생할 수 있습니다.

MEF에서 제공하는 기능

MEF에서는 사용 가능한 구성 요소를 명시적으로 등록하는 대신 컴퍼지션을 통해 구성 요소를 암시적으로 검색하는 방법을 제공합니다. 파트라고 하는 MEF 구성 요소는 종속성(가져오기라고 함) 및 파트를 통해 사용 가능한 기능(내보내기라고 함)을 선언적으로 지정합니다. 파트가 만들어지면 MEF 컴퍼지션 엔진에서는 다른 파트에서 사용 가능한 기능으로 가져오기를 충족합니다.

이 방법을 사용하면 이전 단원에서 설명된 문제가 해결됩니다. MEF 파트는 기능을 선언적으로 지정하고 런타임에 검색할 수 있으므로 응용 프로그램에서는 하드 코드된 참조 또는 취약한 구성 파일이 없어도 파트를 활용할 수 있습니다. MEF를 사용할 경우 응용 프로그램에서는 파트를 초기화하거나 파트의 어셈블리를 로드하지 않아도 메타데이터에 기반하여 파트를 검색 및 조사할 수 있습니다. 따라서 확장이 로드되어야 하는 시점 및 방식을 신중하게 지정할 필요는 없습니다.

파트는 내보내기를 제공할 뿐 아니라 가져오기를 지정할 수 있으며, 가져오기는 다른 파트에 의해 채워집니다. 이는 파트 간 통신을 쉽게 수행할 수 있게 하고 코드 팩터링의 품질을 높여 줍니다. 예를 들어, 여러 구성 요소에 공통적인 서비스를 개별 파트에 포함하고 쉽게 수정하거나 교체할 수 있습니다.

MEF 모델에서는 특정 응용 프로그램 어셈블리에 대한 강한 종속성을 요구하지 않으므로 응용 프로그램 간에 확장을 재사용할 수 있습니다. 또한 응용 프로그램과 상관없이 확장 구성 요소를 테스트하는 테스트 도구를 쉽게 개발할 수도 있습니다.

MEF를 사용하여 작성된 확장 가능한 응용 프로그램에서는 확장 구성 요소에서 채울 수 있는 가져오기를 선언하고, 응용 프로그램 서비스를 확장에 노출하기 위해 내보내기도 선언할 수 있습니다. 각 확장 구성 요소에서는 내보내기 외에 가져오기도 선언할 수 있습니다. 이와 같이 확장 구성 요소는 자체적으로 자동 확장될 수 있습니다.

MEF를 사용할 수 있는 경우

MEF는 .NET Framework 4의 필수 요소이며 .NET Framework가 사용되는 모든 경우에 사용할 수 있습니다. MEF는 Windows Forms, WPF 또는 다른 기술을 사용하는 클라이언트 응용 프로그램이나 ASP.NET을 사용하는 서버 응용 프로그램에서 사용할 수 있습니다.

MEF 및 MAF

이전 버전의 .NET Framework에는 응용 프로그램이 확장을 격리하고 관리할 수 있도록 설계된 MAF(Managed Add-in Framework)가 도입되어 있습니다. MEF의 초점이 검색 가능성, 확장성 및 이식성에 맞춰져 있는 데 반해 MAF는 확장 격리와 어셈블리 로드 및 언로드에 집중하여 MEF보다는 약간 높은 수준에 초점을 두고 있습니다. 두 프레임워크는 원활하게 상호 운용되며 한 응용 프로그램에서 두 프레임워크 모두를 활용할 수 있습니다.

SimpleCalculator: 예제 응용 프로그램

MEF로 수행할 수 있는 작업을 확인하는 가장 간단한 방법은 간단한 MEF 응용 프로그램을 빌드하는 것입니다. 이 예제에서는 SimpleCalculator라는 아주 간단한 계산기를 빌드합니다. SimpleCalculator의 목표는 "5+3" 또는 "6-2" 같은 기본 산술 명령을 받은 후 올바른 해답을 반환하는 콘솔 응용 프로그램을 만드는 것입니다. MEF를 사용하면 응용 프로그램 코드를 변경하지 않아도 새 연산자를 추가할 수 있습니다.

이 예제에 대한 전체 코드를 다운로드하려면 SimpleCalculator 샘플을 참조하십시오.

참고참고

SimpleCalculator의 목적은 실제 사용 시나리오를 제공하는 것이 아니라 MEF의 개념 및 구문을 소개하는 데 있습니다.MEF에서 제공하는 대부분의 기능을 활용하는 응용 프로그램은 일반적으로 SimpleCalculator보다 복잡합니다.보다 광범위한 예제를 보려면 Codeplex에서 Managed Extensibility Framework를 참조하십시오.

시작하려면 Visual Studio 2010에서 SimpleCalculator라는 새 콘솔 응용 프로그램 프로젝트를 만듭니다. MEF가 있는 System.ComponentModel.Composition 어셈블리에 대한 참조를 추가합니다. Module1.vb 또는 Program.cs를 열고 System.ComponentModel.Composition 및 System.ComponentModel.Composition.Hosting에 대한 Imports 또는 using 문을 추가합니다. 이 두 네임스페이스에는 확장 가능한 응용 프로그램을 개발하는 데 필요한 MEF 형식이 포함되어 있습니다. Visual Basic에서 Module1 모듈을 선언하는 줄에 Public 키워드를 추가합니다.

컴퍼지션 컨테이너 및 카탈로그

MEF 컴퍼지션 모델의 핵심은 사용 가능한 모든 파트를 포함하고 컴퍼지션을 수행하는 컴퍼지션 컨테이너입니다. 컴퍼지션이란 가져오기를 내보내기에 일치시키는 것을 가리킵니다. 가장 일반적인 유형의 컴퍼지션 컨테이너는 CompositionContainer이며 SimpleCalculator 예제에서도 이 컨테이너를 사용합니다.

Visual Basic의 Module1.vb에 Program이라는 public 클래스를 추가합니다. Module1.vb 또는 Program.cs의 Program 클래스에 다음 줄을 추가합니다.

Dim _container As CompositionContainer
private CompositionContainer _container;

사용 가능한 파트를 검색하기 위해 컴퍼지션 컨테이너에서는 카탈로그를 활용합니다. 카탈로그는 일부 소스에서 검색된 파트를 사용할 수 있게 해 주는 개체입니다. MEF에서는 제공된 형식, 어셈블리 또는 디렉터리에서 파트를 검색할 수 있도록 카탈로그를 제공합니다. 응용 프로그램 개발자는 새 카탈로그를 만들어 웹 서비스와 같은 다른 소스에서 파트를 쉽게 검색할 수 있습니다.

Program 클래스에 다음 생성자를 추가합니다.

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

ComposeParts를 호출하면 컴퍼지션 컨테이너가 특정 파트 집합을 구성하게 됩니다. 이 예제에서 파트는 Program의 현재 인스턴스입니다. 그러나 Program에는 채워야 할 가져오기가 없기 때문에 이 시점에서는 어떠한 일도 일어나지 않습니다.

특성이 지정된 가져오기 및 내보내기

먼저 Program이 계산기를 가져오도록 합니다. 이렇게 하면 Program에 전달될 콘솔 입력 및 출력 같은 사용자 인터페이스를 계산기의 논리와 분리할 수 있습니다.

Program 클래스에 다음 코드를 추가합니다.

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

calculator 개체의 선언은 비정상이 아니지만 이 개체는 ImportAttribute 특성으로 데코레이트됩니다. 이 특성은 가져올 대상을 선언합니다. 즉, 이 특성은 개체를 구성할 때 컴퍼지션 엔진에 의해 채워집니다.

모든 가져오기에는 가져오기와 일치될 내보내기를 결정하는 계약이 있습니다. 계약은 명시적으로 지정된 문자열이거나 해당 형식으로부터 MEF에서 자동으로 생성될 수 있습니다. 이 예제에서 사용되는 형식은 ICalculator 인터페이스입니다. 일치하는 계약과 함께 선언된 모든 계약은 이 가져오기를 충족합니다. calculator 개체의 형식은 사실 ICalculator이지만 반드시 이 형식일 필요는 없습니다. 계약은 가져오는 개체의 형식과 독립적입니다. 이 예제에서는 typeof(ICalculator)를 제거해도 됩니다. 사용자가 명시적으로 지정하지 않는 한 MEF에서는 자동으로 계약이 가져오기의 형식에 기반하는 것으로 가정합니다.

아주 간단한 이 인터페이스를 모듈 또는SimpleCalculator 네임스페이스에 추가합니다.

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

ICalculator를 정의했으므로 이를 구현하는 클래스가 필요합니다. 모듈이나 SimpleCalculator 네임스페이스에 다음 클래스를 추가합니다.

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

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

}

이 내보내기는 Program의 가져오기와 일치됩니다. 내보내기가 가져오기와 일치하려면 내보내기에 동일한 계약이 있어야 합니다. typeof(MySimpleCalculator)에 기반하는 계약으로 내보내면 불일치가 발생하고 가져오기는 채워지지 않습니다. 계약은 정확히 일치해야 합니다.

이 어셈블리에서 사용 가능한 모든 파트로 컴퍼지션 컨테이너가 채워지기 때문에 MySimpleCalculator 파트를 사용할 수 있게 됩니다. Program의 생성자에서 Program 개체에 대한 컴퍼지션을 수행하면 해당 가져오기가 MySimpleCalculator 개체로 채워집니다. 이 개체는 채우기에 사용하기 위해 만들어집니다.

사용자 인터페이스 계층(Program)에서는 별도로 알고 있어야 할 사항이 없습니다. 따라서 Main 메서드에서 나머지 사용자 인터페이스 논리를 채울 수 있습니다.

Main 메서드에 다음 코드를 추가합니다.

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

이 코드에서는 단순히 입력 한 줄을 읽고 결과에 대해 ICalculator의 Calculate 함수를 호출합니다. 이 결과는 콘솔에 다시 쓰여집니다. Program에서 필요한 코드는 이것이 전부입니다. 나머지 모든 작업은 파트에서 발생합니다.

추가 가져오기 및 ImportMany

SimpleCalculator가 확장 가능하려면 연산 목록을 가져와야 합니다. 일반적인 ImportAttribute 특성은 단 하나의 ExportAttribute로 채워집니다. 사용 가능한 내보내기가 둘 이상 있으면 컴퍼지션 엔진에서 오류를 생성합니다. ImportManyAttribute 특성을 사용하여 개수에 관계없이 여러 개의 내보내기로 채울 수 있는 가져오기를 만들 수 있습니다.

다음 작업 속성을 MySimpleCalculator 클래스에 추가합니다.

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

Lazy<T, TMetadata>는 내보내기에 대한 간접 참조를 포함하도록 MEF에서 제공하는 형식입니다. 이 컬렉션에서 내보낸 개체 자체를 가져올 뿐 아니라 내보내기 메타데이터 또는 내보낸 개체를 설명하는 정보를 가져올 수 있습니다. 각 Lazy<T, TMetadata>에는 실제 연산을 나타내는 IOperation 개체와 해당 메타데이터를 나타내는 IOperationData 개체가 포함됩니다.

다음과 같은 단순한 인터페이스를 모듈이나 SimpleCalculator 네임스페이스에 추가합니다.

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

이 경우 각 작업의 메타데이터는 해당 작업을 나타내는 +, -, * 등의 기호입니다. 다른 연산을 추가로 사용할 수 있게 하려면 다음 클래스를 모듈이나 SimpleCalculator 네임스페이스에 추가합니다.

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

ExportAttribute 특성은 이전과 동일하게 작동합니다. ExportMetadataAttribute 특성은 메타데이터를 이름/값 쌍의 형태로 내보내기에 연결합니다. Add 클래스는 IOperation을 구현하지만 IOperationData를 구현하는 클래스는 명시적으로 정의되지 않습니다. 대신 제공된 메타데이터의 이름을 기반으로 하는 속성을 사용하여 MEF에서 암시적으로 클래스를 만듭니다. 이는 MEF에서 메타데이터에 액세스하는 여러 방법 중 하나입니다.

MEF에서 컴퍼지션은 순환적입니다. 앞에서 Program 개체를 명시적으로 구성했습니다. 이 개체는 실제 형식이 MySimpleCalculator인 ICalculator를 가져옵니다. 그런 다음 MySimpleCalculator는 IOperation 개체 컬렉션을 가져오고, 이 가져오기는 MySimpleCalculator가 만들어질 때 Program의 가져오기와 함께 동시에 채워집니다. Add 클래스에서 추가 가져오기를 선언한 경우 이 가져오기 역시 채워져야 합니다. 채워지지 않은 가져오기가 있으면 컴퍼지션 오류가 발생합니다. 그러나 가져오기를 선택적인 것으로 선언하거나 가져오기에 기본값을 할당할 수는 있습니다.

계산기 논리

앞에서 여러 파트를 배치했으므로 이제 남은 것은 계산기 논리 자체입니다. 다음 코드를 MySimpleCalculator 클래스에 추가하여 Calculate 메서드를 구현합니다.

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

첫 단계에서는 입력 문자열을 구문 분석하여 왼쪽 및 오른쪽 피연산자와 연산자 문자로 분류합니다. foreach 루프에서 operations 컬렉션의 모든 멤버를 조사합니다. 이러한 개체는 Lazy<T, TMetadata> 형식이며 해당 메타데이터 값 및 내보낸 개체에는 각각 Metadata 속성 및 Value 속성을 사용하여 액세스할 수 있습니다. 이 경우, IOperationData 개체의 Symbol 속성이 일치하는 것으로 확인되면 계산기는 IOperation 개체의 Operate 메서드를 호출하고 결과를 반환합니다.

계산기를 완료하려면 문자열에서 숫자가 아닌 첫 번째 문자의 위치를 반환하는 도우미 메서드도 필요합니다. 다음 도우미 메서드를 MySimpleCalculator 클래스에 추가합니다.

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

이제 프로젝트를 컴파일 및 실행할 수 있습니다. Visual Basic에서 Public 키워드를 Module1에 추가했는지 확인합니다. 콘솔 창에서 "5+3"과 같은 더하기 연산을 입력하면 계산기에서 결과를 반환합니다. 다른 연산자의 경우 모두 "연산을 찾을 수 없음!"이라는 메시지가 발생합니다.

새 클래스를 사용하여 SimpleCalculator 확장

계산기가 작동하므로 새 연산을 추가하는 것은 쉬운 작업입니다. 모듈이나 SimpleCalculator 네임스페이스에 다음 클래스를 추가합니다.

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

프로젝트를 컴파일하고 실행합니다. "5-3"과 같은 빼기 연산을 입력합니다. 이제 계산기에서 더하기와 함께 빼기도 지원합니다.

새 어셈블리를 사용하여 SimpleCalculator 확장

소스 코드에 클래스를 추가하는 작업은 아주 간단하지만 MEF에서는 파트에 대한 응용 프로그램 고유 소스 외부를 확인하는 기능을 제공합니다. 이 기능을 시연하려면 DirectoryCatalog를 추가하여 자체 어셈블리 외에 디렉터리에서도 파트를 검색하도록 SimpleCalculator를 수정해야 합니다.

Extensions라는 새 디렉터리를 SimpleCalculator 프로젝트에 추가합니다. 디렉터리는 솔루션 수준이 아니라 프로젝트 수준에서 추가해야 합니다. 그런 다음 ExtendedOperations라는 새 클래스 라이브러리 프로젝트를 솔루션에 추가합니다. 새 프로젝트는 별도의 어셈블리로 컴파일됩니다.

ExtendedOperations 프로젝트에 대한 프로젝트 속성 디자이너를 열고 컴파일 또는 빌드 탭을 클릭합니다. SimpleCalculator 프로젝트 디렉터리의 Extensions 디렉터리(..\SimpleCalculator\Extensions\)를 가리키도록 빌드 출력 경로 또는 출력 경로를 변경합니다.

Module1.vb 또는 Program.cs에서 다음 줄을 Program 생성자에 추가합니다.

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

예제 경로를 Extensions 디렉터리의 경로로 바꿉니다. 이 절대 경로는 디버깅 용도로만 사용할 수 있습니다. 프로덕션 응용 프로그램에서는 상대 경로를 사용합니다. DirectoryCatalog는 Extensions 디렉터리의 모든 어셈블리에서 발견한 모든 파트를 컴퍼지션 컨테이너에 추가합니다.

ExtendedOperations 프로젝트에서 SimpleCalculator 및 System.ComponentModel.Composition에 대한 참조를 추가합니다. ExtendedOperations 클래스 파일에서 System.ComponentModel.Composition에 대한 Imports 또는 using 문을 추가합니다. Visual Basic에서 SimpleCalculator에 대한 Imports 문도 추가합니다. 그런 다음 ExtendedOperations 클래스 파일에 다음 클래스를 추가합니다.

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

계약이 일치하도록 하려면 ExportAttribute 특성의 형식은 ImportAttribute와 동일해야 합니다.

프로젝트를 컴파일하고 실행합니다. 새 Mod(%) 연산자를 테스트합니다.

결론

이 항목에서는 MEF의 기본 개념에 대해 다루었습니다.

  • 파트, 카탈로그 및 컴퍼지션 컨테이너

    파트 및 컴퍼지션은 MEF 응용 프로그램의 기본 빌딩 블록입니다. 파트는 개체 자체를 비롯한 값을 가져오거나 내보내는 모든 개체이고, 카탈로그는 특정 소스에 있는 파트의 컬렉션을 제공합니다. 컴퍼지션 컨테이너는 카탈로그에서 제공하는 파트를 사용하여 컴퍼지션을 수행하고 가져오기를 내보내기에 바인딩합니다.

  • 가져오기 및 내보내기

    가져오기 및 내보내기는 구성 요소가 통신하는 방법입니다. 구성 요소는 가져오기를 통해 특정 값 또는 개체가 필요함을 지정하고, 내보내기를 통해서는 특정 값을 사용할 수 있음을 지정합니다. 각 가져오기는 계약을 통해 내보내기 목록과 일치됩니다.

추가 정보

이 예제에 대한 전체 코드를 다운로드하려면 SimpleCalculator 샘플을 참조하십시오.

자세한 내용 및 코드 예제는 Managed Extensibility Framework를 참조하십시오. MEF 형식 목록은 System.ComponentModel.Composition 네임스페이스를 참조하십시오.

변경 기록

날짜

변경 내용

이유

2010년 7월

단계를 업데이트했습니다. VB에 대한 누락된 단계를 추가했습니다. 샘플을 다운로드할 수 있는 링크를 추가했습니다.

고객 의견