Scott Hanselman

Back to Basics: var != Dim

June 25, '08 Comments [36] Posted in Back to Basics | Programming | VB
Sponsored By

Someone said today after seeing the C# var operator for implicitly typed variables, "Oh, now C# has Dim." Well, not exactly. Not even close.

I like what this C# tutorial says about var.

...[var is] a new keyword that means, "I want to declare a variable, but I’m too lazy to write out its type."

One way to look at the power of VB's Dim operator is to say,

Dim kind of means, "I want to declare a variable but I can't tell you much about how it behaves until much later."

Dim lets you do actual late-binding while in C# (today) you do late-binding with reflection. I wanted to find a way to clearly express this in a very visceral sample.

A friend wanted to change a value in the CustomDocumentProperties in Word and see that change reflected in an updated field. In the document properties dialog below, you can see there's a "CustomProperty1" that has the value of "Scott" in it. Then in the document, there's a Field that is bound to that property. It's not meant to be a mail merge, but more of a MadLibs kind of a thing for writing applications that update forms and templates within Word or Excel documents.

image

His language of choice is C#, so he started off in C#. He added a reference to the Microsoft.Office.Interop.Word PIA (Primary Interop Assembly) and fought with the  system for some hours. After a while, I got enlisted, and after I figured out that the specific COM interface he needed was a late-bound IDispatch interface, we were able to crack with the Reflection.

I'm starting to think of Reflection much the way I think about Regular Expressions. If you have to solve your Problem with Reflection, you may just end up with Problems, plural!

Notice a few things. First, the need for some of those obvious strings and booleans to be of type Object. Notice all the System.Reflection.Missing.Values passed by reference. Most of all, notice the custom GetCustomPropertyValue and SetCustomPropertyValue that had to use Reflection.

  ApplicationClass WordApp = new ApplicationClass();
WordApp.Visible = true;
object missing = System.Reflection.Missing.Value;
object readOnly = false;
object isVisible = true;
object fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\..\NewTest.doc");
Microsoft.Office.Interop.Word.Document aDoc = WordApp.Documents.Open(
ref fileName,ref missing,ref readOnly,ref missing,
ref missing,ref missing,ref missing,ref missing,
ref missing,ref missing,ref missing,ref isVisible,
ref missing,ref missing,ref missing,ref missing);

aDoc.Activate();

string propertyValue = GetCustomPropertyValue(aDoc, "CustomProperty1");
SetCustomPropertyValue(aDoc, "CustomProperty1", "Hanselman");

foreach (Range r in aDoc.StoryRanges)
{
r.Fields.Update();
}
}

public string GetCustomPropertyValue(Document doc, string propertyName)
{
object oDocCustomProps = doc.CustomDocumentProperties;
Type typeDocCustomProps = oDocCustomProps.GetType();
object oCustomProp = typeDocCustomProps.InvokeMember("Item",
BindingFlags.Default |
BindingFlags.GetProperty,
null, oDocCustomProps,
new object[] { propertyName });

Type typePropertyValue = oCustomProp.GetType();
string propertyValue = typePropertyValue.InvokeMember("Value",
BindingFlags.Default |
BindingFlags.GetProperty,
null, oCustomProp,
new object[] { }).ToString();

return propertyValue;
}

public void SetCustomPropertyValue(Document doc, string propertyName, string propertyValue)
{
object oDocCustomProps = doc.CustomDocumentProperties;
Type typeDocCustomProps = oDocCustomProps.GetType();
typeDocCustomProps.InvokeMember("Item",
BindingFlags.Default |
BindingFlags.SetProperty,
null, oDocCustomProps,
new object[] { propertyName, propertyValue });
}

0103mf1 There's a great article from 7 (yes SE7EN) years ago on Dr. Dobb's about Invoking COM Components from C# that provided me this diagram. The RCW (Runtime Callable Wrapper) sits in front of the COM Object and makes my reflection calls work.

Sure, I could have created some IDL and laid out an IDispatch implementation for these CustomDocumentProperties, but that's getting REALLY involved. Actually, there's supposed to be an implementation for CustomDocumentProperties but the MSDN Sample fails with this:

"Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.Office.Core.DocumentProperties'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{2DF8D04D-5BFA-101B-BDE5-00AA0044DE52}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE))."

Competing with that non-working MSDN sample is this KB article from 2007 that provided the bulk of the yucky, but straightforward reflection code.

Why is this a Back to Basics post? Well, two fold. First, COM is Old and it's Basic. Seriously, though, the secondly (and only) reason is that, in my opinion, C# 3.0 is lousy for this kind of late-bound, COM-interop, Office Automation work.

Don't believe me? Here's the same code in VB. See the named parameters on the Open()? Notice the late-bound COM stuff just works without Reflection? (I've got Option Strict to Off for this code)

Dim WordApp = New Microsoft.Office.Interop.Word.ApplicationClass
WordApp.Visible = True

Dim fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\..\..\NewTest.doc")
Dim aDoc As Document = WordApp.Documents.Open(FileName:=fileName, ReadOnly:=True, Visible:=True)
aDoc.Activate()

Dim PROP = "CustomProperty1"
Dim propertyValue = aDoc.CustomDocumentProperties(PROP).Value
aDoc.CustomDocumentProperties(PROP).Value = "Hanselman"
For Each r As Range In aDoc.StoryRanges
r.Fields.Update()
Next

VB.NET is really well suited for this kind of thing, and my buddy will likely use it in this scenario.

I hear this big difference in dynamism will change for the next version of C#. I'll talk to the team and try to get some details or rewrite my C# sample in C#.Next. I've also asked John Lam to help me write this sample in IronRuby. I suppose it'd look nice in PowerShell also.

The Point is, and until some future date, var != Dim.

The Back to Basics thing to remember is that the language you know might not always be suited for the problem you have. Just because you CAN solve a problem in a certain language doesn't mean that you should. If something REALLY doesn't feel right in a certain language, ask around, perhaps there's another that makes more sense for what you're trying to do.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Wednesday, June 25, 2008 12:44:37 AM UTC
they arent the same, but they sure are confusing. Why not just type out the "type" it does seem like everything comes full circle. Pretty soon our code will look like VBA/VBScript and we wont even have to declare the variables if we don't want to. I just spent years getting away from all that, why even start to go back, if even just in the way it looks when you look at code?

sometimes it is already bad enough with String vs string, BOOL vs bool (C++) why make it any more complicated, I just don't get it.

Wednesday, June 25, 2008 1:05:41 AM UTC
Good post. I haven't had to do much COM interop, but I'll have to remember that.
Wednesday, June 25, 2008 1:06:22 AM UTC
Perhaps this article could be called var <> Dim? :-)
Wednesday, June 25, 2008 1:06:59 AM UTC
Here's what I ended up having to do in order to Update All Fields in headers and footers and sections in Word problematically, in case someone needs to search for it later.

aDoc.Fields.Update()
For Each s In aDoc.StoryRanges
s.Fields.Update()
Next
For Each t In aDoc.TablesOfContents
t.Update()
Next
For Each r In aDoc.Sections
For Each f In r.Footers
f.Range.Select()
f.Range.Fields.Update()
Next
For Each h In r.Headers
h.Range.Select()
h.Range.Fields.Update()

Next
Next
WordApp.ActiveWindow.View.Type = WdViewType.wdMasterView
WordApp.ActiveWindow.View.Type = WdViewType.wdPrintPreview
Wednesday, June 25, 2008 1:17:14 AM UTC
I think there's another reason for 'var': with LINQ you end up with anonymous types all the time, so you couldn't type them out even if you wanted to. Here's a simple self-contained example:

var aboutMyArgs = (from a in Environment.GetCommandLineArgs() select new { slashy = a.StartsWith("/"), dashy = a.StartsWith("--") }).First();


Here, aboutMyArgs doesn't seem to have a type you could type.
Wednesday, June 25, 2008 1:31:11 AM UTC
I was sure worried when I first saw "var" appear into C#, for its resemblance with the dreaded "variant" type in old-days VBA. Since then, I have realized it had nothing to do with compromising type-safety, and it's actually pretty useful. Besides making LINQ possible, as pointed by Max C, Coding Horror has a recent post on how it is useful in getting rid of redundancies: why write the lengthy
MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
when you can have:
var md5 = new MD5CryptoServiceProvider();
Wednesday, June 25, 2008 1:55:26 AM UTC
Great article. In a similar vein, a coworker was recently having some problems working with a device driver that was provided as an unmanaged DLL. He was having an extremely hard time getting anything to work using extern calls in C#. We got the driver working by writing some managed C++ that could work with the unmanaged DLL. We could've been pulling our hair out for quite some time trying to do the same thing in C#.

C# developers like to knock VB.NET but you definitely provide a good example where VB.NET makes C# look extremely cumbersome
Wednesday, June 25, 2008 3:26:08 AM UTC
Hey Scott
About that UPDATE ALL FIELDS sample. Just a comment. There are a handful of field types in Word, that if you update them like this, you'll end up popping dialogs (for instance, the ASK field is one that comes to mind).

If you're code doesn't mind prompting the user during your update, have at it. But if there's lots of them, that might be a PITA for people.

Good post though. C# looks like a real pain for any sort of Office automation work.
Good to know.
Darin
Wednesday, June 25, 2008 3:31:50 AM UTC
Good point about ASK fields. Is there a better way?
Wednesday, June 25, 2008 3:40:13 AM UTC
Unfortunately, not that I've found.
About the only thing you can do is to enumerate all the fields in each collection, check each field's TYPE property, and if it's a UI driven field, don't update it.

I know these can cause problems....

wdFieldFillIn
wdFieldAsk
wdFieldFormTextInput

but what makes this issue even more fun (believe me, I know WAY too much about Word automation), is that you can actually have documents with +nested+ fields.

If the outer field is a normal field, but it +contains+ one of these prompting type fields, updating the outer field will force an update of the inner field, and, pop, up comes a dialog.

So it's not an easy thing to get around no matter what way you go.
Darin
Wednesday, June 25, 2008 4:07:32 AM UTC
Any self-respecting VB developer will set Option Explicit On, Option Strict On, and Option Infer On for their IDE, every project, and every code file. Under these conditions Dim == var.
Wednesday, June 25, 2008 4:17:43 AM UTC
"Problems, plural!" ... Are you a fan of the movie Four Rooms??? Sounds like you might be.

Sayed Ibrahim Hashimi
Wednesday, June 25, 2008 4:23:11 AM UTC
Jason - How would that self-respecting VB Developer then solve the problem we had interacting with Word (COM)? ;)

Sayed - Nice catch! I'm glad you noticed!
Wednesday, June 25, 2008 5:00:36 AM UTC
One thing I always end up doing is using GetObject to create Office objects, rather than adding a reference to the COM library itself. That way you get a certain degree of version independence, and you can always check for the existence of methods and functions at runtime.

We usually end up with VB.Net wrapper classes for the Office objects, that way they can get consumed in a C# project without all the boilerplate reflection code, and we still get a certain degree of intellisense despite using late bound GetObject calls under the covers that return a plain System.Object rather than something like Microsoft.Office.Interop.Word.Document.
Wednesday, June 25, 2008 5:17:48 AM UTC
I'm nost definitely opposed to the statement '...[var is] a new keyword that means, "I want to declare a variable, but I’m too lazy to write out its type."'

Why should using 'var' mean I am lazy? While var may simply be compiler trickery, it would not be possible to use anonymous types if we didn't have this feature. I use anonymous types all the time, especially when writing up sample code to test something real quick or to answer someone's post in a forum. It's much easier than having to write an entire class just so that I can have some data to throw in a Grid. Not to mention anonymous types are needed with LINQ. It (var) also lends itself to better code readability, which should be a goal of every developer.

I know that wasn't the main focus of this post. Thanks for explaining the difference between var and Dim. I have just started learning VB and posts like this are quite helpful when I continually try to justify to myself why I should learn it.
Wednesday, June 25, 2008 6:16:14 AM UTC
I would propose that being lazy is a GOOD thing. Lazy, to me, means, "unwilling to do unnecessary work." In my twisted mind, lazy can ultimately mean "efficient and effective." I don't think it was meant as an insult in this case, certainly I didn't mean it as one when I quoted it. ;) I'm all for var and use it all the time, except when I want to be slightly more explicit.
Wednesday, June 25, 2008 7:56:29 AM UTC
I, like Jason, believe in "Option Strict On" wherever possible.

However there are limits to this and I would think this is the perfect situation for a partial class in which the "option strict off" switch should be thrown.

This allows you to apply "option strict off" to a single method if need be.
Wednesday, June 25, 2008 8:01:08 AM UTC
Hi Scott,
Lazy is such a poor word to explain it to be honest with you. And of course in this case can mean efficient and effective, and it means it. However I find it mostly as a better way to communicate idea by not cluttering my code:

var someVeryPowerfulSuperhero = myPowerfulSuperHeroFactory.getPowerfulSuperhero("HULK");

I already gave descriptive name to my variable, I don't need to embellish it with MightyStuff.SuperHeroes.IPowerfulSuperhero; it reads alright and I have all the IDE intelli powers to execute some MightyDeed.

I don't need to introduce new namespace using, importing possibly hundreds of unwanted classes/interfaces/... names, keeping my intellisense clear and simple while still nicely communicating what I want to do.

And I don't need to repeat myself: SomeUsefulClass somethingUseful = new SomeUsefulClass(); I know it's SomeUsefulClass what's the value of adding the class name in front of the variable when you gonna give the name of the class in the same line anyway?

Yes I know this wasn't the topic but you just had to use word lazy, did you? :)

Cheers,
Mihailo
Wednesday, June 25, 2008 11:47:35 AM UTC
"var" is such a bad choice of keyword - it has so many other connotations - I really wish it had been called "infer" instead - at least assignments would read more fluidly.
Wednesday, June 25, 2008 1:15:32 PM UTC
A PowerShell example,

Not as "Nice" as I hoped as I needed Reflection also as the CustomDocumentProperties COM Object does not expose TypeInfo.

$msWord = New-Object -com word.application
$doc = $msword.Documents.Open('c:\test1\test.doc')
$doc.Activate()

$type = $doc.CustomDocumentProperties.gettype()
$prop = $type.InvokeMember("Item","GetProperty",$null,$doc.CustomDocumentProperties,"CustomProperty1")

$PropType = $doc.CustomDocumentProperties.gettype()

"Old : " + $Proptype.InvokeMember("Value","GetProperty",$null,$prop,$null)
$Proptype.InvokeMember("Value","SetProperty",$null,$prop,"Mow2")

"New :" + $Proptype.InvokeMember("Value","GetProperty",$null,$prop,$null)

"Updated fields :" + $doc.StoryRanges |% {$_.Fields.Update()}

Enjoy,
Greetings /\/\o\/\/
Wednesday, June 25, 2008 1:39:52 PM UTC
Even --if-- var were the same as Dim, I'd welcome it as I just cannot stand the VB.NET language as a whole or Dim (converted from it to C# about 3 years ago) (way too verbose) . I can't wait till the day of "Microsoft officially no longer supports VB.NET and new languages such as F# and C# are now preferred".

However I know that's just not reality....or could it be? Please?
Dave Schinkel
Wednesday, June 25, 2008 2:23:46 PM UTC
"Here's what I ended up having to do in order to Update All Fields in headers and footers and sections in Word problematically, in case someone needs to search for it later." <- problematically (!) I love this comment!

OT, Here is something interesting in VB.NET with Option Strict. If you turn it ON at the project level, you can not shut it off at the file level. Makes a nice experiment for those who might be interested. I know it works this way in VS2005, but I haven't tried VS2003 or VS2008.
Wednesday, June 25, 2008 2:35:12 PM UTC
I took lazy in the Larry Wall sense of the word - we recognize that a computer's way better at doing repetitive work than we are, we just need to do a little setup to trick it into doing the grunt work for us.
Wednesday, June 25, 2008 3:04:29 PM UTC
You said a mouthful. I'm a total VB enthusiast with occassional forays into C# as the client demands but I seriously pity any person masocistic enough to do COM InterOp with C#. It's just not worth it. These are two technologies which more or less grew up together. Many of the "unappreciated" features of VB are critical to working with legacy COM systems - C#'s language purity fights you every step of the way in that regard.

As for type inference, it's certainly not about laziness. LINQ and anonymous types aside it's about the DRY principal - how is it useful to a developer and especially the compiler to specify the type of a variable initialized at declaration twice? Read the Essentials of Interaction Design 3.0 and you'll find new appreciation for software not asking dumb questions of your users.
Anthony D. Green
Wednesday, June 25, 2008 3:53:48 PM UTC

Robz: "If you turn it ON at the project level, you can not shut it off at the file level. "

Erm Robz.... thats not right

The following code works fine under VS2005 and VS2008 with project-level option strict turned on :
-------------------------------------------------------------
Option Strict Off
Module Module1
Sub Main()
Dim WordApp As New Object
WordApp.Visible = True
End Sub
End Module
-------------------------------------------------------------
...and removing the "Option Strict Off" statement brings the error "Option Strict On disallows late binding"

Wednesday, June 25, 2008 4:51:52 PM UTC
You are correct. I misspoke. The difference is here:

Dim obj2 = New ExternalHandler()

I should be able to do this, but I am not able to. If I go to Project Properties and change Option Strict to Off, the application will compile.

----
Option Strict Off
Option Explicit Off

Public Class TestOptionStrict

Public Sub OptionStrictTest()
Dim thisVar As Integer
Dim obj As Object = New ExternalHandler()
Dim obj2 = New ExternalHandler()
thisVar = 1000 ' Declared variable does not generate error.
' Attempting to convert Double to Integer generates a COMPILER ERROR.
thisVar = 1234567890.9876542 ' causes ERROR
' Late-bound call generates a COMPILER ERROR.
obj.Method1() ' causes ERROR
obj2.Method1()
End Sub

Private Sub OptionExplicitTest()
Dim thisVar As Integer
thisVar = 10
' The following assignment produces a COMPILER ERROR because
' the variable is not declared and Option Explicit is On.
thisInt = 10 ' causes ERROR
End Sub

End Class
----
Thursday, June 26, 2008 12:02:38 PM UTC
Good point about choosing the right tool to do the job. One of the cool thing about type safty in c# is higher productivity in average because the compiler is able to spot a lot of bugs that might be hard to track down but that's kinda negated if you need to write 3 times as much code, and you need to use type unsafe reflection. I do most of my coding in c# but I'll keep this in mind for when ever I have to do office automation.

Another fun thing is how thing get mixed up when they are introduced at the same time. I've seen several argue that var is a nice thing because we need anonymous types for LINQ but that's actually mixing things up.

var x = new Dictionary<string,string>();

Is not saying "I want x to be of an anonymous type". It's saying use type inferences to determine that the type of x should be Dictionary<string,string>() that's not anonymous. if it were you would not be able to do:

from pair in x
select new {Name = x.Key, First= x.Value[1]};

An anonymous type do not implement IEnumerable<KeyValuePair<string,string>> but the Dictionary given above does.

The LINQ statement returns an IEnumeration<anonymous type> where the anonymous type have two properties Name and First.
In the var statement there's no anonymous type and in the select part of the LINQ there's no var so clearly anonymous type is not a valid argument for var. The compiler trickery needed to make anonymous type work (type inferrence) can be used to make the var statement possible but the var statement is not needed for anonymous types to work.

Thursday, June 26, 2008 1:06:16 PM UTC
the language war's are unproductive.
It takes the tools out of the hands of the programmer by saying one language is superior to another.
I love the CLR because I can chose the best language to express my ideas.
I love shoving arrays build in c# and vb.net to APL.
Save a lot of code in language that was built for array processing.

Thomas
Thursday, June 26, 2008 2:04:23 PM UTC
Hi Scott,

Actually "Dim lets you do actual late-binding while ..." should read "Dim ... as Object lets you do actual late-binding while ..."

Martin
Thursday, June 26, 2008 8:49:13 PM UTC
C#.Next... wha? Looking forward to that!
Mike
Friday, June 27, 2008 10:03:02 AM UTC
Hi Scott,

You might find the VSTO Power Tools interesting. I remember the announcement back in february but since I'm not a regular C# coder, I didn't look into it.

http://blogs.msdn.com/andreww/archive/2008/02/21/vsto-vsta-power-tools-v1-0.aspx

http://www.microsoft.com/downloads/details.aspx?FamilyId=46B6BF86-E35D-4870-B214-4D7B72B02BF9&displaylang=en

Hope this helps
DM
Monday, June 30, 2008 6:18:10 PM UTC
I'm not sure I want C# to become better at this, but this is clearly an area where VB.NET has some merit. The PIAs could be improved by MS though, with more overloads etc.

I ran into similar problems with automation of Adobe InDesign, and ended up creating massive amounts of wrapper classes. I used CodeSmith and Reflection to create the classes, which also enabled me create a very typesafe, and somewhat interface based library. This was back in the 1.1 days, but now with generics, extension methods etc. I've started to experiment with something like this:


public static class WordExtensions
{

public static Document Open(this Documents documents, string filename, DocumentOpenOptions options)
{
object filenameobj = filename;
return documents.Open(ref filenameobj,
ref options.ConfirmConversionsValue,
ref options.ReadOnlyValue,
ref options.AddToRecentFilesValue,
..........
}
}

public class DocumentOpenOptions
{
internal object ConfirmConversionsValue = Missing.Value;

public bool? ConfirmConversions
{
set { ConfirmConversionsValue = value.HasValue ? (object)value : Missing.Value; }
}

internal object ReadOnlyValue = Missing.Value;

public bool? ReadOnly
{
set { ReadOnlyValue = value.HasValue ? (object)value : Missing.Value; }
}

internal object AddToRecentFilesValue = Missing.Value;

public bool? AddToRecentFiles
{
set { AddToRecentFilesValue = value.HasValue ? (object)value : Missing.Value; }
}
}


..which allows constructs like this:


Document aDoc = WordApp.Documents.Open("", new DocumentOpenOptions { ReadOnly = false,AddToRecentFiles=true,ConfirmConversions=false });


Using code generation and reflection, this kind of code can be generated en masse.

Monday, June 30, 2008 8:58:54 PM UTC
Gunteman - Cool! Did you release them?
Scott Hanselman
Wednesday, July 02, 2008 5:20:53 PM UTC
This is an excellent example of why different languages exist in the first place. I much prefer C# over VB, and I hate late binding (anytime I have to review code from a coworker who leaves Option Strict Off they get an earful). But there are some cases, like COM, where late binding is the only option; and when that is the case, you are much better off using a language that is built to handle it fluently, rather than a language which fights it tooth and nail. This is why I am extremely wary of the attempt to build late binding into the next version of C# (http://blogs.msdn.com/charlie/archive/2008/01/25/future-focus.aspx). I use C# because I like strong typing and early binding; I don't want late-binding to be a first class citizen, because most of the time it is a very bad idea. In the rare cases that I need late binding, I can always use VB. Since we have the ability in .NET for multiple languages to interoperate together, why do we feel the need to make all languages look exactly alike? Lets let individual languages have their own strengths so that developers can continue to choose the best tool for the job.
Wednesday, July 02, 2008 8:43:38 PM UTC
Well said, David.

If "dynamic invocation" becomes something that is explicitly activated through the use of the DLR, then fine, but don't adapt the entire language to it.

Scott, I replied to your previous post, but I think the reply is stuck in some kind of review system. Should links be avoided?
Tuesday, July 22, 2008 8:28:24 PM UTC
Hi, I'm not a VB.NET specialist, but couldn't we say that var != dim except when var == dim?

What I mean by this is just that if you set "option explicit" aren't these two kinds of snippets equivalent?

var req = from table where condition...;

and

Option explicit
...
Dim req = From table Where condition...;


(I've used Linq for the sample, but any case where the "Dimmed" variable type is immediately inferred would have worked).

Apart from this, I just wanted to confirm the usefulness of VB.net when dealing with Word PIA. And I'm in general rather reluctant at using VB... just a matter of taste (Office Interop is the only case where I code using VB).
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.