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
public interface IEnumerable : IEnumerable{ IEnumerator GetEnumerator();}public interface IEnumerator : IEnumerator{ bool MoveNext(); T Current { get; }}
In earlier versions of C#, an IEnumerable