One of the disadvantages of C# compared with VB is its lack of support for parameterized properties. Instead, parameterized properties in C# are exposed as normal method calls prefixed with "get_" and "set_". This is particularly apparent when working with the Office object model as it exposes many such properties, the majority being indexers on collection interfaces. To make matters worse, some collection interfaces have array indexers rather than indexer properties, which make indexing inconsistent between collection types. Since many of the parameters are optional or accept varying types, we lose many of the strong typing benefits of the C# language. The end result is inconsistent, inelegant, and error prone code.
Let's take the Documents collection in Word for example. That interface defines the Item property with a single variant (i.e. object) argument. According to the MSDN documentation, that argument value should be an integer or a string. Retrieving a document by integer index looks something like:
object index = 1;
Word.Document doc = Application.Documents.get_Item(ref index);
MessageBox.Show("Name: " + doc.Name);
Notice the use of 1 instead of 0; remember that the Office object model uses 1-based indexing. Also note that we have to pass the index by reference, requiring an extra object on the stack. This is a particular quirk of the Word object model, but one that makes for even more inelegant code.
Retrieving a document by name looks something like:
object index = "WordDocument1.docx";
Let's clean up these examples using the Office Interop API Extensions, one of the recently released VSTO Power Tools. For collections, the libraries expose a set of common Item() extension methods with strongly-typed arguments. With those we can rewrite our examples:
Word.Document doc = Application.Documents.Item(1);
Word.Document doc = Application.Documents.Item("WordDocument1.docx");
Unfortunately, not all of the collections could be extended in the initial release of the VSTO Power Tools. You will find the Word and Excel object models to be most complete, with key collections extended across the rest of the Office suite. (If there are particular collections which you think should be extended in the future, please let us know so that we can prioritize them appropriately.)
Indexers are not the only parameterized properties which were extended, however. Let's take another example from Excel:
Excel.Worksheet sheet = InnerObject;
Excel.Range range1 = sheet.get_Range("A1:B1", System.Type.Missing);
Excel.Range range2 = sheet.get_Range("A1", "B2");
In this example, we're retrieving two ranges from an Excel Worksheet. The Range property (as defined by the COM interface) accepts two parameters, both of which are of variant type and the second of which is also marked optional. Since C# doesn't support optional parameters, we have to pass System.Type.Missing to indicate the absence of the second value. Since both values are variant types, we do not have any compile-time type checking. Again, we can use the extensions provided by the Office Interop API Extensions to improve the code:
Excel.Range range1 = sheet.Range("A1:B1");
Excel.Range range2 = sheet.Range("A1", "B2");
Not only have we eliminated the ugly "get_" and the need to specify System.Type.Missing, our arguments are conveniently strongly-typed. These may seem like little things, but over the long term I think the improved readability and additional compile-time support will make for a better Office development experience for the C# developer.