First, you have Program import a calculator. 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. Add the following code to the Program class:
<Import(GetType(ICalculator))>
Public Property calculator As ICalculator
[Import(typeof(ICalculator))]
public ICalculator calculator;
Notice that the declaration of the calculator object is not unusual, but that it is decorated with the Import attribute. This attribute declares something to be an import; that is, it will be filled by the composition engine when the object is composed.
Every import has a contract, which determines what exports it will be matched with. 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. Any export declared with a matching contract will fulfill this import. Note that while the type of the calculator object is in fact ICalculator, this is not required; the contract is independent from the type of the importing object. (In this case, you could leave out the typeof(ICalculator). MEF will automatically assume the contract to be based on the type of the import unless you specify it explicitly.)
Next, 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);
}
Now that you have defined ICalculator, you need a class that implements it. 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
{
}
Here is the export that will match the import in Program. In order to match it, it must have the same contract. 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.
Since the composition container will be populated with all the parts available in this assembly, the MySimpleCalculator part will be available. 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.
The user interface layer (Program) does not need to know anything else. You can therefore fill in the rest of the user interface logic in 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));
}
}
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. That is all the code you need in Program. All the rest of the work will happen in the parts.