Walkthrough: Embed types from managed assemblies in Visual Studio
If you embed type information from a strong-named managed assembly, you can loosely couple types in an application to achieve version independence. That is, your program can be written to use types from any version of a managed library without having to be recompiled for each new version.
Type embedding is frequently used with COM interop, such as an application that uses automation objects from Microsoft Office. Embedding type information enables the same build of a program to work with different versions of Microsoft Office on different computers. However, you can also use type embedding with fully managed solutions.
After you specify the public interfaces that can be embedded, you create runtime classes that implement those interfaces. A client program can embed the type information for the interfaces at design time by referencing the assembly that contains the public interfaces and setting the Embed Interop Types
property of the reference to True
. The client program can then load instances of the runtime objects typed as those interfaces. This is equivalent to using the command line compiler and referencing the assembly by using the EmbedInteropTypes compiler option.
If you create a new version of your strong-named runtime assembly, the client program doesn't have to be recompiled. The client program continues to use whichever version of the runtime assembly is available to it, using the embedded type information for the public interfaces.
In this walkthrough, you:
- Create a strong-named assembly with a public interface containing type information that can be embedded.
- Create a strong-named runtime assembly that implements the public interface.
- Create a client program that embeds the type information from the public interface and creates an instance of the class from the runtime assembly.
- Modify and rebuild the runtime assembly.
- Run the client program to see that it uses the new version of the runtime assembly without having to be recompiled.
Note
Your computer might show different names or locations for some of the Visual Studio user interface elements in the following instructions. The Visual Studio edition that you have and the settings that you use determine these elements. For more information, see Personalizing the IDE.
Conditions and limitations
You can embed type information from an assembly under the following conditions:
- The assembly exposes at least one public interface.
- The embedded interfaces are annotated with
ComImport
attributes andGuid
attributes with unique GUIDs. - The assembly is annotated with the
ImportedFromTypeLib
attribute or thePrimaryInteropAssembly
attribute, and an assembly-levelGuid
attribute. The Visual C# and Visual Basic project templates include an assembly-levelGuid
attribute by default.
Because the primary function of type embedding is to support COM interop assemblies, the following limitations apply when you embed type information in a fully-managed solution:
- Only attributes specific to COM interop are embedded. Other attributes are ignored.
- If a type uses generic parameters, and the type of the generic parameter is an embedded type, that type cannot be used across an assembly boundary. Examples of crossing an assembly boundary include calling a method from another assembly or deriving a type from a type defined in another assembly.
- Constants are not embedded.
- The System.Collections.Generic.Dictionary<TKey,TValue> class does not support an embedded type as a key. You can implement your own dictionary type to support an embedded type as a key.
Create an interface
The first step is to create the type equivalence interface assembly.
In Visual Studio, select File > New > Project.
In the Create a new project dialog box, type class library in the Search for templates box. Select either the C# or Visual Basic Class Library (.NET Framework) template from the list, and then select Next.
In the Configure your new project dialog box, under Project name, type TypeEquivalenceInterface, and then select Create. The new project is created.
In Solution Explorer, right-click the Class1.cs or Class1.vb file, select Rename, and rename the file from Class1 to ISampleInterface. Respond Yes to the prompt to also rename the class to
ISampleInterface
. This class represents the public interface for the class.In Solution Explorer, right-click the TypeEquivalenceInterface project, and then select Properties.
Select Build on the left pane of the Properties screen, and set the Output path to a location on your computer, such as C:\TypeEquivalenceSample. You use the same location throughout this walkthrough.
Select Build > Strong naming on the left pane of the Properties screen, and then select the Sign the assembly check box. In the Strong name key file, select Browse.
Navigate to and select the key.snk file you created in the TypeEquivalenceInterface project, and then select OK. For more information, see Create a public-private key pair.
Open the ISampleInterface class file in the code editor, and replace its contents with the following code to create the
ISampleInterface
interface:using System; using System.Runtime.InteropServices; namespace TypeEquivalenceInterface { [ComImport] [Guid("8DA56996-A151-4136-B474-32784559F6DF")] public interface ISampleInterface { void GetUserInput(); string UserInput { get; } } }
Imports System.Runtime.InteropServices <ComImport()> <Guid("8DA56996-A151-4136-B474-32784559F6DF")> Public Interface ISampleInterface Sub GetUserInput() ReadOnly Property UserInput As String End Interface
On the Tools menu, select Create Guid, and in the Create GUID dialog box, select Registry Format. Select Copy, and then select Exit.
In the
Guid
attribute of your code, replace the sample GUID with the GUID you copied, and remove the braces ({ }).In Solution Explorer, expand the Properties folder and select the AssemblyInfo.cs or AssemblyInfo.vb file. In the code editor, add the following attribute to the file:
[assembly: ImportedFromTypeLib("")]
<Assembly: ImportedFromTypeLib("")>
Select File > Save All or press Ctrl+Shift+S to save the files and project.
In Solution Explorer, right-click the TypeEquivalenceInterface project and select Build. The class library DLL file is compiled and saved to the specified build output path, for example C:\TypeEquivalenceSample.
Create a runtime class
Next, create the type equivalence runtime class.
In Visual Studio, select File > New > Project.
In the Create a new project dialog box, type class library in the Search for templates box. Select either the C# or Visual Basic Class Library (.NET Framework) template from the list, and then select Next.
In the Configure your new project dialog box, under Project name, type TypeEquivalenceRuntime, and then select Create. The new project is created.
In Solution Explorer, right-click the Class1.cs or Class1.vb file, select Rename, and rename the file from Class1 to SampleClass. Respond Yes to the prompt to also rename the class to
SampleClass
. This class implements theISampleInterface
interface.In Solution Explorer, right-click the TypeEquivalenceInterface project and select Properties.
Select Build on the left pane of the Properties screen, and then set the Output path to the same location you used for the TypeEquivalenceInterface project, for example, C:\TypeEquivalenceSample.
Select Build > Strong naming on the left pane of the Properties screen, and then select the Sign the assembly check box. In the Strong name key file, select Browse.
Navigate to and select the key.snk file you created in the TypeEquivalenceInterface project, and then select OK. For more information, see Create a public-private key pair.
In Solution Explorer, right-click the TypeEquivalenceRuntime project and select Add > Reference.
In the Reference Manager dialog, select Browse and browse to the output path folder. Select the TypeEquivalenceInterface.dll file, select Add, and then select OK.
In Solution Explorer, expand the References folder and select the TypeEquivalenceInterface reference. In the Properties pane, set Specific Version to False if it is not already.
Open the SampleClass class file in the code editor, and replace its contents with the following code to create the
SampleClass
class:using System; using TypeEquivalenceInterface; namespace TypeEquivalenceRuntime { public class SampleClass : ISampleInterface { private string p_UserInput; public string UserInput { get { return p_UserInput; } } public void GetUserInput() { Console.WriteLine("Please enter a value:"); p_UserInput = Console.ReadLine(); } } }
Imports TypeEquivalenceInterface Public Class SampleClass Implements ISampleInterface Private p_UserInput As String Public ReadOnly Property UserInput() As String Implements ISampleInterface.UserInput Get Return p_UserInput End Get End Property Public Sub GetUserInput() Implements ISampleInterface.GetUserInput Console.WriteLine("Please enter a value:") p_UserInput = Console.ReadLine() End Sub End Class
Select File > Save All or press Ctrl+Shift+S to save the files and project.
In Solution Explorer, right-click the TypeEquivalenceRuntime project and select Build. The class library DLL file is compiled and saved to the specified build output path.
Create a client project
Finally, create a type equivalence client program that references the interface assembly.
In Visual Studio, select File > New > Project.
In the Create a new project dialog box, type console in the Search for templates box. Select either the C# or Visual Basic Console App (.NET Framework) template from the list, and then select Next.
In the Configure your new project dialog box, under Project name, type TypeEquivalenceClient, and then select Create. The new project is created.
In Solution Explorer, right-click the TypeEquivalenceClient project and select Properties.
Select Build on the left pane of the Properties screen, and then set the Output path to the same location you used for the TypeEquivalenceInterface project, for example, C:\TypeEquivalenceSample.
In Solution Explorer, right-click the TypeEquivalenceClient project and select Add > Reference.
In the Reference Manager dialog, if the TypeEquivalenceInterface.dll file is already listed, select it. If not, select Browse, browse to the output path folder, select the TypeEquivalenceInterface.dll file (not the TypeEquivalenceRuntime.dll), and select Add. Select OK.
In Solution Explorer, expand the References folder and select the TypeEquivalenceInterface reference. In the Properties pane, set Embed Interop Types to True.
Open the Program.cs or Module1.vb file in the code editor, and replace its contents with the following code to create the client program:
using System; using System.Reflection; using TypeEquivalenceInterface; namespace TypeEquivalenceClient { class Program { static void Main(string[] args) { Assembly sampleAssembly = Assembly.Load("TypeEquivalenceRuntime"); ISampleInterface sampleClass = (ISampleInterface)sampleAssembly.CreateInstance("TypeEquivalenceRuntime.SampleClass"); sampleClass.GetUserInput(); Console.WriteLine(sampleClass.UserInput); Console.WriteLine(sampleAssembly.GetName().Version.ToString()); Console.ReadLine(); } } }
Imports System.Reflection Imports TypeEquivalenceInterface Module Module1 Sub Main() Dim sampleAssembly = Assembly.Load("TypeEquivalenceRuntime") Dim sampleClass As ISampleInterface = CType( _ sampleAssembly.CreateInstance("TypeEquivalenceRuntime.SampleClass"), ISampleInterface) sampleClass.GetUserInput() Console.WriteLine(sampleClass.UserInput) Console.WriteLine(sampleAssembly.GetName().Version) Console.ReadLine() End Sub End Module
Select File > Save All or press Ctrl+Shift+S to save the files and project.
Press Ctrl+F5 to build and run the program. Note that the console output returns the assembly version 1.0.0.0.
Modify the interface
Now, modify the interface assembly, and change its version.
In Visual Studio, select File > Open > Project/Solution, and open the TypeEquivalenceInterface project.
In Solution Explorer, right-click the TypeEquivalenceInterface project and select Properties.
Select Application on the left pane of the Properties screen, and then select Assembly Information.
In the Assembly Information dialog box, change the Assembly version and File version values to 2.0.0.0, and then select OK.
Open the SampleInterface.cs or SampleInterface.vb file, and add the following line of code to the
ISampleInterface
interface:DateTime GetDate();
Function GetDate() As Date
Select File > Save All or press Ctrl+Shift+S to save the files and project.
In Solution Explorer, right-click the TypeEquivalenceInterface project and select Build. A new version of the class library DLL file is compiled and saved to the build output path.
Modify the runtime class
Also modify the runtime class and update its version.
In Visual Studio, select File > Open > Project/Solution, and open the TypeEquivalenceRuntime project.
In Solution Explorer, right-click the TypeEquivalenceRuntime project and select Properties.
Select Application on the left pane of the Properties screen, and then select Assembly Information.
In the Assembly Information dialog box, change the Assembly version and File version values to 2.0.0.0, and then select OK.
Open the SampleClass.cs or SampleClass.vb file, and add the following code to the
SampleClass
class:public DateTime GetDate() { return DateTime.Now; }
Public Function GetDate() As DateTime Implements ISampleInterface.GetDate Return Now End Function
Select File > Save All or press Ctrl+Shift+S to save the files and project.
In Solution Explorer, right-click the TypeEquivalenceRuntime project and select Build. A new version of the class library DLL file is compiled and saved to the build output path.
Run the updated client program
Go to the build output folder location and run TypeEquivalenceClient.exe. Note that the console output now reflects the new version of the TypeEquivalenceRuntime
assembly, 2.0.0.0, without the program being recompiled.