devxlogo

COM Interop Gets Much Better in C# 4.0

COM Interop Gets Much Better in C# 4.0

he first article in this series on C#’s new features looked at the language’s new support for dynamic lookup and for named and optional parameters in C#. This article and the concluding article in this series cover the enhanced COM Interop features and support for variance in C# 4.0.

Enhanced COM Interop Features

C# 4.0 provides much improved support for COM interoperability, making it far easier to call COM objects from your .NET code. You need fewer typecasts, the ref keyword is no longer required, and you no longer have to depend on Primary Interop Assemblies, or PIAs. In essence, C# 4.0 provides a lot of new features that are specifically targeted to make your life easier when working with COM interop. The new dynamic lookup and support for named and optional parameters discussed in the previous article help improve the experience of interoperating with COM APIs such as the Office Automation APIs.

For example, here’s the pre-4.0 C# code you need to write to open a Word document:

using Word = Microsoft.Office.Interop.Word;namespace COMInterop{   class Program   {      static void Main(string[] args)      {         Word.Application wordApplication =             new Word.Application() { Visible = true };         object missingValue = System.Reflection.Missing.Value;         object readOnlyValue = true;         object fileName = @"C:\DevX.docx";         wordApplication.Documents.Open(ref fileName, ref             missingValue, ref readOnlyValue,            ref missingValue, ref missingValue,             ref missingValue, ref missingValue,             ref missingValue, ref missingValue,            ref missingValue, ref missingValue,            ref missingValue, ref missingValue,            ref missingValue,ref missingValue);      }   }}

In the Open call in the last line, you can see that you need to pass a number of optional parameters just to satisfy the method call. With the introduction of optional and named parameters in C# 4.0, you can do the same with much less code.

Here’s the code you can use to open a word document in C# 4.0:

using Word = Microsoft.Office.Interop.Word;namespace COMInterop{   class Program   {      static void Main(string[] args)      {         Word.Application wordApplication = new             Word.Application() {Visible = true};            wordApplication.Documents.Open(@"C:\DevX.docx",             ReadOnly: true);      }   }}

Improved Performance

PIAs are generated from the COM interfaces. You can use such assemblies in your application’s code to interoperate with COM objects in a strongly typed manner; however, they’re heavy, consume a huge amount of memory, and can slow down the application’s performance considerably. Instead, the interop assembly generated by compiling the preceding code would contain only the interop code actually used by your application, which reduces the size of your assembly drastically and improves the application’s performance.

Dynamic Import

The majority of the COM methods accept and return variant types. These are represented in the PIAs as objects. So, when you use these methods, you need to use appropriate casts. With C# 4.0 however, you use the dynamic keyword instead of object in the method signatures of the COM methods. Therefore, you can now call such methods without having to cast the types as you did earlier.

Consider the following code where you need to use casts to set a value in a particular cell in an Excel document.

 ((Excel.Range)excelObj.Cells[5, 5]).Value =    "This is sample text";

The preceding code uses casts, but in C# 4.0, you can eliminate the casts?thanks to the dynamic keyword! Here’s a new way to achieve the same result:

excelObj.Cells[5, 5].Value = "This is sample text";

As a more complete example, here’s some typical C# 3.x code to save an Excel document in C#.

using Excel = Microsoft.Office.Interop.Excel;namespace COMInterop{   class Program   {      static void Main(string[] args)      {         var excelApplication = new Excel.Application();         excelApplication.Visible = true;         dynamic excelWorkBook =             excelApplication.Workbooks.Add(            System.Reflection.Missing.Value);         Excel.Worksheet wkSheetData = (            Excel.Worksheet)excelWorkBook.ActiveSheet;         excelWorkBook.SaveAs("Testfile.xls",             System.Reflection.Missing.Value,             System.Reflection.Missing.Value,            System.Reflection.Missing.Value,             System.Reflection.Missing.Value,             System.Reflection.Missing.Value,            Excel.XlSaveAsAccessMode.xlShared,             System.Reflection.Missing.Value,             System.Reflection.Missing.Value,            System.Reflection.Missing.Value,             System.Reflection.Missing.Value,             System.Reflection.Missing.Value);      }   }}

In C# 4.0, you no longer need to use the missing values and explicit casts shown above. Here’s the same functionality in C# 4.0 without the usage of explicit casts and missing values:

using Excel = Microsoft.Office.Interop.Excel;namespace COMInterop{   class Program   {      static void Main(string[] args)      {         var excelApplication = new Excel.Application();         excelApplication.Visible = true;         dynamic excelWorkBook = excelApplication.Workbooks.Add();         Excel.Worksheet wkSheetData = excelWorkBook.ActiveSheet;          excelWorkBook.SaveAs(            "Testfile.xls",            AccessMode: Excel.XlSaveAsAccessMode.xlShared);      }   }}
Author’s Note: Note that COM has an entirely different programming model than managed code. To call COM methods, the C# compiler allowed you to pass parameters by value and would generate temporary variables to hold those values. The variables were discarded when the method completed.

Support for Variance

With C# 4.0, you can specify in (input only) and out (return only) parameters on generic types. These parameters can then be passed as only input parameters or used only as return values for such types.

Support for variance in C# 4.0 can be twofold: covariance and contravariance. The terms invariance, covariance and contravariance deserve a bit of explanation. Bill Wagner writes: “A return value or parameter is invariant if you must use the exact match of the formal type name. A parameter is covariant if you can use a more derived type as a substitute for the formal parameter type. A return value is contravariant if you can assign the return type to a variable of a less derived type than the formal parameter.”

Author’s Note: I won’t cover invariant parameters any further here?you’re already more than familiar with those from earlier versions of C#.

Covariance

Covariance is easier to understand with a simple example. Note that string is a specific type, whereas object is a generic type. So, string is covariant to object. Now, let?s see how .NET 4.0 supports covariance. The interface IEnumerable is defined in .NET 4.0 as shown below:

public interface IEnumerable : IEnumerable{   IEnumerator GetEnumerator();}public interface IEnumerator : IEnumerator{  bool MoveNext();  T Current { get; }}

In earlier versions of C#, an IEnumerable wasn?t an IEnumerable. Notice the out parameter in the preceding IEnumerator definition, which indicates that generic T can occur only in the output position; otherwise, the compiler will report an error. The interface is covariant in T meaning that IEnumerable

is also IEnumerable provided P is derived from Q. So, a sequence of strings is also a sequence of objects. Hence, the following statement is perfectly valid:

IEnumerable someObj = new List();

The next example illustrates how you can make use of covariance in C# 4.0:

namespace Test{   class Base    {      //Methods and Properties of the Base Class   }   class Derived : Base    {       //Methods and Properties of the Derived Class   }   class Program   {      delegate T TestFunction();      static void Main(string[] args)      {         TestFunction derivedObj = () => new Derived();         TestFunction baseObj = derivedObj;      }   }}

Contravariance

Type parameters in generic interfaces in C# 4.0 can have an in modifier that allows them to occur in input positions only, for example:

public interface IComparer{  public int Compare(T left, T right);}

So, a comparer that can compare objects can compare strings as well. This is called contravariance. A good example of contravariance is the Equals() method or the CompareTo() method. If you have a method that can compare two instances of a base class, you can use the same method to compare two instances of a derived class as well. You can easily store the result of any method calls in an instance of type object, because method return types in C# are contravariant.

Here’s a contravariant counterpart of the preceding example:

namespace Test{   class Base    {      }   class Derived : Base    {      }   class Program   {      delegate void TestDelegate(T a);      static void Main(string[] args)      {         TestDelegate baseObj = (obj) => {             System.Console.WriteLine(obj); };         TestDelegate derivedObj = baseObj;      }   }}
Author’s Note: Type variance works only on interfaces and delegate types. Variance can be applied only if there is a reference conversion between the type arguments. So, IEnumerable is not an IEnumerable because int is a value type and object is a reference type. In other words, conversion of an integer to an object type is a boxing conversion and not a reference conversion?and therefore is not an example of variance.

As you can see, C# 4.0’s new features make working with Primary Interop Assemblies much more pleasant. The support for optional parameters means you don’t have to explicitly pass “missing” values to functions in place of optional parameters you either don’t have or don’t need. The support for named parameters means you don’t have to pass values in a specific order (as long as you use the names), and the support for variance eliminates many of the tedious and error-prone casts that were formerly required. All in all, these are welcome and useful changes.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist