Pattern-Based XML Literals Prototype: Cross-Platform Mobile Apps w/ Xamarin.Forms

In my last post I introduced the idea of pattern-based XML literals—XML literals which construct arbitrary types other than XDocument/XElement/XAttribute/etc. And as promised, I have recorded a video showing off the practical benefits of this design when using Xamarin.Forms from VB.NET. If you’re not familiar with Xamarin.Forms it’s an API that abstracts over the differences between mobile platforms like Android and iOS to make it easier to create and maintain cross-platform apps while sharing the maximum amount of code. Watch as I explore the Xamarin VB sample app from the official Xamarin samples repo using this new prototype:

As you can see, it’s pretty empowering to be able to use XAML everywhere.

  • I can leverage my XAML skills from WPF to a degree and be more productive.
  • If I’m looking at the documentation or a sample or blog post (most likely written from a C# developer) that uses XAML, I can use that XAML in my code rather than first translating it to VB imperative code.
  • In places where today you’d possibly have to give up markup for code like dynamic view creation, I can still use declarative XAML even if it’s in the middle of a VB method (this scenario comes up in writing VS extensions to the WPF editor too).
  • Even with object initializers there can be situations where you have to fallback to imperative syntax like using attached properties but that’s not an issue using markup.
  • I get to embed any VB expression inside my markup in places where that expressiveness is a benefit over XAML.

WPF XAML vs Xamarin XAML

Xamarin has its own flavor of XAML. It’s essentially the same on the surface (syntactically) as what’s used in WPF and Silverlight (and UWP) but it’s not the same XAML engine and it uses different types internally. Not just in the “Controls” or as Xamarin calls them “Views” but in the infrastructure itself:

  • So rather than DependencyObject and DependencyProperty, Xamarin has BindableObject and BindableProperty.
  • Xamarin has its own base types for type converters, and markup extensions, etc.

So, even though both it and WPF use the XAML syntax (which is XML based), Xamarin.Forms is powered by a completely different engine entirely. Possibly as a consequence of this the XAML used by Xamarin.Forms doesn’t support VB.NET. Which would normally mean you have to create your UI imperatively, rather than declaratively. VB guru Klaus Löffelmann wrote about how to do this back in 2017 on the VB team blog. It was a good post but the no-XAML part was kind of a bummer. This prototype changes all of that!

The Xamarin differences actually highlight another benefit to being pattern-based. Because the language understands patterns—certain methods with certain names and shapes existing—rather than specific types (like DependencyObject) it’s possible to apply the feature to a completely different system than WPF. If the feature were just WPF XAML literals built to understand the specifics of the WPF infrastructure you couldn’t turn around and apply it to Xamarin but by being pattern-based you can do that easily.

Under the hood

As I indicate in the video, the compiler knows XML and how to translate certain idioms in XML into call-outs to extension methods that encapsulate all of the Xamarin-specific knowledge. I’m pretty sure the pattern will change as I explore more but the first page’s code:

Dim page = <ContentPage Title="My First Android App!" BindingContext=<%= Model %>>
<StackLayout VerticalOptions="Center">
Text="Welcome to Xamarin.Forms with Visual Basic .NET!"
<Entry Text="{Binding Name}"/>
<Button Text="Next" Clicked="NextButton_Click"/>

Translates to this:

' Passing `Nothing` as the first arg of Set<AttributeName> methods indicates
' that the compiler couldn't convert the attribute text to the indicated type
' at compile time. If I had written `FontSize="16"`, that line would read:
' `temp1_1_1.SetFontSize(16.0, "16.0", Nothing)`
' The third argument is the value produced by an embedded expression, if it
' doesn't naturally convert to the first arg type at compile time. If I had
' written `Text=<%= New Binding("Name") %>`, that line would read:
' `temp1_1_2.SetText(Nothing, Nothing, New Binding("Name"))`
Dim temp1 = New ContentPage
temp1.Title = "My First Android App!"
temp1.BindingContext = Model
Dim temp1_1 = New StackLayout
temp1_1.SetVerticalOptions(Nothing, "Center", Nothing)
Dim temp1_1_1 = New Label
temp1_1_1.HorizontalTextAlignment = TextAlignment.Center
temp1_1_1.SetFontSize(Nothing, "Medium", Nothing)
temp1_1_1.Text = "Welcome to Xamarin.Forms with Visual Basic .NET!"
Dim temp1_1_2 = New Entry
temp1_1_2.SetText(Nothing, "{Binding Name}", Nothing)
Dim temp1_1_3 = New Button
temp1_1_3.Text = "Next"
AddHandler temp1_1_3.Clicked, AddressOf NextButton_Click
Dim page = temp1

As you can see, the compiler knows how to translate attribute values to enum members at compile-time. So if I misspell and enum member I’ll get a compile-time error:

Likewise the compiler knows how to translate an attribute value to a delegate creation, so that too gets compile-time validation:

Both of these make it easy to give in-context completion support in the IDE for these cases.

The keen observer may have also noticed that when I selected name CalculateButton in the value of the Name attribute at around the 2:45 mark in the video that references to CalculateButton in code were highlighted. This is another example of the power of Roslyn. Because the compiler binds that text to the CalculateButton variable, the IDE understands it as such and features like Go-to-Definition work automatically!


I accidentally edited out the part where I show what turns a regular XML literal into a pattern based one and it’s a magic "clr-namespace:" XML namespace import at the top of each file (could also be in a project-level import). If I comment out that import you can see that I get conversion errors because the XML reverts to producing an XElement as it does today:

I expect to find a better way to enable the feature though because of some trade-offs with that approach. This is just a draft version of the pattern and I fully expect it to change as I explore other shapes of XML to apply it to and weigh certain experiences that could be built.

For example, right now the pattern respects both a SetChildContent and AddChildContent bridge methods so that the compiler can report an error if you include more than one child element in a situation where only one child is allowed, but maybe it’s better to simplify the pattern and enforce things like that in a Roslyn Analyzer (let me know what you think).

If you’re really curious as to what each of these bridge methods looks like, I’ve uploaded the content of the XmlExtensions.vb file from the sample app I showed in the video as a Gist. It’s just what I wrote out while exploring the pattern and shouldn’t be taken as the “best way” to write such methods and I played around with different approaches. Just take it all with a grain of salt.

Next steps

I know Xamarin.Forms has been around for quite awhile now but honestly I haven’t been as excited to try it out until now. I can see some places where the Xamarin team made extremely reasonable choices in their content model (like how templating works). I’m anxious to see how I can broaden the pattern to work well with those patterns and to just generally explore mobile app development from VB.NET.

Fun Fact: Due to Windows Mobile and Compact Framework and then Windows Phone and Windows 10 Mobile, I have not owned a phone that I couldn’t program in VB.NET in almost 15 years. I’m excited to continue that tradition.

One thing this video didn’t show off was tooling. Because it’s all just the VB.NET language and the IDE is powered by Roslyn, getting things like completion, go-to-definition, F1 help, etc. to work in such XML literals is much easier than it would have been pre-VS2015. Also, there’s this other unrelated prototype I plan to show off… 4-5 prototypes from now that could really improve on both desktop apps using WPF/WinForms and mobile apps using Xamarin too. So whether it’s to show off better tooling, to show off another prototype, or just because I’m having fun exploring Xamarin.Forms I don’t think this will be the last video I make featuring this topic.

As for my next post it’ll be a continuation of this series about this prototype but instead applied to web development. I think you’re going to love it so look out for it.

Until then, as always, thanks for reading, thanks for watching, please share this post, and let me know your thoughts in the comments below and/or on Twitter.



P.S. As in my last series, I’ll upload all of my notes from implementing the prototype in a GitHub issue after the last post in the series. Don’t sweat the specifics of the pattern too much, they’re subject to and very likely to change. This post took me much longer than intended before the first few iterations of the video were over 30-minutes! It took some re-recording and aggressive editing to get it down to 5.X minutes, but I think that’s optimal for online viewing and sharing. I hope you agree!

“I have made this [letter] longer than usual because I have not had time to make it shorter.”
-Blaise Pascal

Pattern-Based XML Literals Prototype: Introduction

Hey all!

I’ve been down in the laboratory since my last post and I’m back with a brand-new prototype. I was working on scripting the first video for this prototype when I realized there’s just a lot of context to set beforehand so I’m taking a break to write a quick introduction.

What do I mean by pattern-based?

When I describe a language feature as “pattern-based” I mean that the feature is designed to work with any code that fits some pattern the compiler knows about rather than a specific limited predefined set of types. Not every feature is pattern based in VB.NET but several of them are. For example:

`For Each` doesn’t require a type implement `IEnumerable` (or that `IEnumerable` even exist) but rather that it meets the Collection pattern. A type is a Collection Type from the perspective of the compiler/language if it has an accessible `GetEnumerator` method that returns a type with a Boolean returning `MoveNext` method and a readable `Current` property and the `Current` property determines the type of the elements of the `For Each`. None of this requires `IEnumerable`, that’s just convenient and it means that library authors can write specialized enumerators which are more efficient in some way, usually by being structures rather than classes.

`Await` is another example. Everyone knows you can await `Task` and `Task(Of T)` but the language doesn’t require that, rather that the type have a `GetAwaiter` method that returns some type with the right set of members on it. If the type meets that pattern it is considered Awaitable. An example of this in action is the `Task.Yield` method which returns a `YieldAwaitable` structure, rather than `Task`.

Query comprehensions (the SQL-like LINQ syntax) aren’t tied to `IEnumerable` or even the `System.Linq.Enumerable` type either, but rather work on Queryable types—types which have a `Select` method that take a delegate that itself takes 1 parameter, the type of which is the element type of the `From` operator and may have other operators like `Where`, etc.

A big win from the pattern approach is that often a type can be made to support a pattern after the fact through an extension method. This is great when you don’t control the library that type is defined in but still want it to work well with new features. C# and F# also use this approach at times to varying degrees (F# turns it up to 11, actually). There are other features that use patterns (and ones that don’t) those are some big examples. Eric Lippert (of course) talked about some examples on his blog and one of the ones that don’t in either VB or C# (occasionally to my chagrin), the `Using` block.


Right now, XML Literals are in the ones that don’t bucket and I’m looking at what possibilities open up if that changes. These first three scenarios are minor. I definitely thought about them but I understand if they’re not top of mind of a lot of other people. They were stops along the train of thought. The last two though are pretty major so feel free to skip ahead if you aren’t interested in trivia.

Minor Scenarios

Windows Runtime/UWP XML

One of the first concrete scenarios I had was back when I was exploring the Windows Runtime/UWP library and I noticed that WinRT defines its own `Windows.Data.Xml.Dom` namespace which is, of course, not the `System.Xml.Linq` namespace that VB XML literals work with and I got to thinking, wouldn’t it be nice if VB could work natively with the WinRT types. If you look at the docs for creating live tiles and sending tile updates, the whole tile template system in Windows 8.x/10 is built on XML and their code examples look like they would benefit from XML literals and XML axis properties.

That’s one idea. It’s good language hygiene but fairly limited.

Rich WPF Message Boxes

I’m a big fan of Alan Cooper “Father of Visual Basic” (big surprise) and specifically his books on user experience like “The Inmates Are Running the Asylum” and “About Face: The Essentials of Interaction Design”. In one of those books he talks about message boxes and points out the ways software provides poorer, less situationally appropriate experiences because the default APIs are “good enough” and programmers just try to squeeze whatever problem they have into what the API provides rather than writing a custom dialog. It’s been years but I recall him showing an example of some program that wants the user to do one of two things and it puts up a dialog box that says something like “How would you like to proceed? Click ‘OK’ to file your taxes jointly or ‘Cancel’ to file separately with a 1099c extension”. Obviously, this choice shouldn’t be in terms of ‘OK’ and ‘Cancel’ but the programmer was too lazy to write the custom one-off dialog. The programmer has delegated mapping concepts from user-domain to the API to the user and that sucks.

The MessageBox.Show in WinForms and the other one in WPF are a little bit more customizable than this but sometimes I call MessageBox.Show and think “I wish I could provide a formatted string as the dialog text, maybe include some bolding or color or an image but the API just takes a string”. Ideally I’d be able to use all of WPF’s TextBlock primitives and just hand that to a generic built-in dialog presenter. I could write one, of course but there’s still some tedium to it because in my code I can’t use XAML, I have to imperatively construct a potentially elaborate object graph and that stinks.

XML-based DSL’s

If you look at the unit tests in the Roslyn source code, you’ll see thousands of examples of things like this:

An actual VB compiler semantic unit test from the Roslyn code base
An actual C# IDE unit test written in VB, presumably to take advantage of those sweet sweet XML literals

The test team created these DSLs for setting up tests for the compiler and the IDE that other code then parses to create the actual test environment. XML has just enough structure vs strings while being flexible around multiline content. It’s neat and in a lot of places convenient to use XML to describe these test cases declaratively but it’s unfortunate that everything that happens in the XML literal is opaque to the compiler so you don’t get any tooling assistance at compile time. You could help this a little with analyzers but there are still seams in the experience.

Major scenarios


In an earlier post I showed you how one could use XML literals to make a basic view engine for ASP.NET Core. It was nice but because it’s just producing an anemic XML DOM there are some limitations on how kick-ass it could be. I’m trying to see how far I can take it.


On the other side of the spectrum we have Xamarin.Forms. I’ve been interested in Xamarin for a few years now but one thing that bugs me is that to use it in VB I have to use code to construct my UI. I can’t use my XAML skills (until now).

So, I made a prototype…

The challenge is, can I come up with some reasonable pattern that would enable an XML literal to construct and/or interact with things other than the XLinq API. I’ve got a first-draft and some notes and a about 2-3 scenarios to show you based on what I came up with that I hope you’ll like. The first one up is the Xamarin scenario and it’ll be the subject of my very next post. I just wanted to get all the background out of the way so I can focus the video and the blog post on the scenario itself rather than general context setting.



Top-Level Code Prototype: Scenario E

At last we’ve come to the last of my planned scenarios for this prototype–a little thing I like to think of as a WYSIWYG Web Service, or WYSIWYGWS for short. A few months back I had recently finished the JSON literals post and video and was anxiously combing my immediate vicinity for more scenarios when Scott Hanselman came along quite serendipitously with his post, Converting an Excel Worksheet into a JSON document with C# and .NET Core and ExcelDataReader to inspired me.

I asked myself “How simple can defining a REST endpoint be?” and here is the result.

Of course there are myriad ways to approach this problem with various trade-offs, this is just one.

One developer on Twitter asked Scott (paraphrasing) “Why not just use PowerShell?”

To which Scott replies, “Very useful, yes, but would require additional PowerShell to get the output JSON I wanted”

And another developer says, what are the benefits of using ExcelDataReader over this PowerShell command?

And Scott reiterates, “Control over the result is all”

And that’s one key aspect to this scenario. Scott had an idea of what shape he wanted at the end and it was just a matter of getting what he had into that shape. And in the video above that’s exactly how I approach the problem as well, I start with that shape and then populate it. Now that’s what I call… Design by Contract! (ba dum chhh)

Another element to the scenario is data that is… uncooperative:

  • You can’t control it
  • You can’t annotate it serialization attributes
  • The shape isn’t right for default serialization
  • It’s coming directly from an external (not .NET objects) data source like a database, mainframe, device, other service, or in this case a file

And that last point is more important than you might think. Every day (on Facebook) I see a lot of developers working on apps that use DataSets as their data layer and/or are using an RDBMS that doesn’t have SQL Server’s fancy `FOR JSON` clause. Without getting into a big argument about whether those folk need to “Get with the times” and “Move everything over to X”, what’s the fastest way for them to Cloud- or Mobile-enable that data? And even if you do have POCOs for your domain-objects, there are a lot of reports in life and not every report deserves its own custom EndOfYearSalesByRegionReportRecordRow class just so you can use JSON serialization.

It’s also just a useful way and crazy easy way to mock up an service end-point. When I was exploring the GitHub Gist API I was very conscious of how my debugging might look to the GitHub service. Was I hitting the live API up too much? Will I hit a request limit? With this I can just go to a service, grab the output, and paste it and develop against that until I’m ready to hit the live service. Ease in testing is its own reward.


In my last post I combined top-level expressions with XML literals to create a basic VB.NET view engine for web pages on ASP.NET Core. In this post I took the same strategy but swapped out XML for JSON literals to make ASP.NET Core REST services a breeze in VB.NET. The approach has trade-offs and shines especially when dealing with one-off data sources such as files, database reports, and objects you can’t modify for serialization but yet still need to end up in a particular shape in the end, as well as testing.

So what about you?

Do you have any data you need to get out of some less that reputable places as painlessly as possible?

See, the Millennium Falcon in this metaphor is the JSON. Or maybe the JSON is Obi-wan. I guess the data is the droids?

Does the low-ceremony abstraction I presented seem like the kind of thing that would make that easier or more attractive for you? Let me know in the comments below, or on Twitter.

Thanks for your time, your shares, and your feedback!



P.S. This is the last scenario 😥 in this series so my next post will be letting you know that the write-up of my design notes from exploring this idea have been posted on GitHub. Thanks for your patience!

Top-Level Code Prototype: Scenario D

In Scenario A I talked about the 1-line program—a single statement that prints the value of some expression to the console.

The next logical mental exercise (for me at least) is, “Gee, what if you get tired of typing Console.WriteLine all the time. Wouldn’t it be great if a program could be a single expression and the printing could all be handled auto-magically?”. I said it was a mental exercise, not a practical one 😊

Outside of making expression evaluator bots really simple that idea isn’t actually great at all, but patterns I came up with to approach it are useful for applications of other project types and many many lines of code. The Process looks something like this:

  1. “What if a source file could be just a single expression?”
  2. “You know what’s an expression in VB? XML!”
  3. XHTML 5 is XML”
  4. <stuff>
  5. This video of me demostraing a VB ASP.NET Core 2.0 MVC app with an XML literal-based “View Engine”:

Special shout-out to book author, former VB MVP, and current Microsoftie on the WinForms team, Klaus Löffelmann for doing the heavy lifting on creating the ASP.NET Core 2.0 Web API project sample I used to make this demo. It really distilled things to the bare minimum which was a huge help to me conceptually and also a great time saver!

What about “Razor”?

Here’s the thing about “Razor” (Is it a code-name, do I need quotes?). It’s an alternate view engine to ASP.NET Web Forms that aims for a particular aesthetic. That aesthetic has value but is definitely designed around C# (not dissimilar from the way Web Forms was designed around VB and Classic ASP). The Razor that is part of ASP.NET MVC 5 supports VB but the experience isn’t a bespoke VB experience. I’m pretty sure it doesn’t support VB XML literals at all (and I understand why) and that whole Code / End Code block syntax, while… well-intended does show the seams a little. And when we talk about ASP.NET Core there’s no vbhtml at all. I’m really not saying this to knock Razor at all. I think it’s cool. And as a guy who spent 5 years spelunking two parsers, I appreciate the challenges of building and maintaining a component that understands the nuances of code while the user is typing—a  problem that is even harder when the language itself is hosting another language nested in it (XML)—while also wanting to provide a great tooling experience. (I appreciate in part because I was working on Roslyn when the ASP.NET team was working on Razor and Web Matrix and they weren’t shy about telling us about the challenges 😉). And new language versions present new conflicts if new features don’t play in the way your view engine was coded to support: I mean, can you imagine what Razor would do if VB added JSON literals?

But, we (VB programmers) are in luck. Because Microsoft already owns an incredibly well-tested, well-oiled, well-maintained component that knows the ins and outs of the VB language and seamlessly transitions between VB, and XML, and interpolated strings, infinitely nested with an incredibly rich IDE experience just deeply integrated into it. It’s always maintained, it can’t fall out of sync with new features, and it’s basically free. It’s called Roslyn; we should use it!

Once again, as I did in Scenario B, I’m asking “What if we invert the problem?” and instead of getting VB into the views, we get views into the VB. Or better yet use what’s already in VB to make something idiomatic and aesthetically pleasing just for us at a comparatively much lower cost?

WWFSD—What Would F# Do?

Over the years since its introduction I’ve been continually impressed with the grace, creativity, self-reliance, and pioneering spirit with which the F# community has approached being unique members of the broader .NET community. It’s incredibly easy to assume that there’s only one way to approach programming and that it’s whatever the newest way Microsoft has released or whatever way C# is doing it because it’s intrinsically good for everyone and the best anyone can come up with for all situations (until the next thing comes out). F# is very good at remaining rooted in its own identity while looking for solutions to the same scenarios that C# or VB coders face but in a way that is complementary to and often strengthened by F#’s unique capabilities.

For example, Razor doesn’t support F# either but when I hear F# aficionados talk about web programming they aren’t talking about Razor, they’re talking about homegrown solutions like Suave and Fable and Giraffe and CloudButt and other made up names that are built with F#’s differences (read: strengths) in mind.

It is my sincere wish that we, the VB community, learn from their many examples and that one day we too have a catalogue of colorful names on a web page somewhere built by us for us. This is my opening contribution to what I hope will be an ongoing conversation.


I’ve still got one more scenario to post and then I’ll do the full write-up on GitHub the ins-and-outs of the proposed top-level code feature that made this video possible. Web programming is just one potentially application of it that I thought up but there’s another guy who’s been thinking on this topic a lot longer than I have, VBAndCs. I really wanted this scenario to be a surprise but VBAndCs saw it all coming in my comments section all the way back in Scenario B.

I have another planned prototype in my head that would go even further on this scenario that I hope to put out later and I’m really looking forward to seeing what awesome tailor-made experiences passionate folks in the VB-Web community like VBAndCs can build given the right language tools.

Share your feedback with me, share the video with your friends. And as always, thanks and see ya later!



Top-Level Code Prototype: Scenario C

Getting away from Console apps and Console paraphernalia, “Scenario C” is all about how top-level code can plug in to more interesting orchestrations.

Here’s a quick 5-minute video showing off top-level code forming the body of a game loop:

I really wanted to re-create the simplicity of my earliest experiments with animation and writing very primitive games in (you guessed it) Qbasic. So I pushed all of boilerplate into a base class in a library and now this one file can accomplish more just based on how the base class decides to run it; it’s pseudo-scripting. A great use of inversion-of-control in action 😊

And this same approach can be used to solve other problems too, depending on your style. Sometimes have situations where a class is dominated by one (or a few) very important methods that maintainers should focus on, particularly in derived types. Does this happen to you and if so, how do you highlight that? Do you just put a comment over it that says ' Keep this method at the top or do you put it in a separate partial file?

I really wanted to do something fun and not-console centric with this scenario. In my next scenario I’m going to expand on this approach to do something even cooler (and remote from Console apps) so look out!

Feedback welcome, please share, thanks!



P.S. Reminder: This is Scenario C. It’s the third of 5. I’ll post the usual GitHub issue with my writeup of all the design details/questions I ran into at a future date.

Top-Level Code Prototype: Scenario B

There’s a lot of excitement these days for interweaving code and rich-text in the same environment. In my last post I touched on this referencing some of the work going on with “Try .NET” and the .NET docs. Other cool approaches to this are Jupyter Notebooks and Xamarin Workbooks. In this video I combine my top-level code prototype with with a proof-of-concept Visual Studio extension I hacked together to show an alternate approach that VB could take:

Special Thanks to the folks at Marked.js for saving me from having to roll my own markdown renderer for this demo.

Finding the balance point

This approach leverages all the flexibility of the Visual Studio editor to give you a rich text experience while sacrificing absolutely none of the capabilities of a full professional IDE.

I love the experience of Notebooks/Workbooks but the trade-off of starting to build-out an experience for VB outside of VS is that you end up spending a lot of cycles rebuilding the capabilities you already have. It starts with “let just have a run-able code block embedded in a page” but the next thing is:

  • But that code block should have the right syntax highlighting; can’t be just black text—that’s weird.
  • And… well, semantic colorization of types would be nice too.
  • Some semantic tooltips could really liven up the place…
  • And wouldn’t it be nice to step through this code one line at a time?
  • Wouldn’t it be nice if you could edit the code.
  • Well now we need completion.
  • Completion isn’t using the right keys, it doesn’t feel right because it doesn’t complete on <space> or <.> or trigger in the right contexts…
  • Hmm, definitely going to want that end-construct-generation… I’m tired of typing “End” blocks.
  • Probably going to want to get squiggles for syntax errors…
  • Hey, do you think we could get a Roslyn analyzer running up here?
  • How does a person graduate this experience out of a document and into a real-project?

Of course these are problems you can solve and have been solved but there are more challenges behind them and it all stems from digging yourself out of the hole you jump into when you start from scratch. This scenario is about inverting the problem and asking “How lightweight and rich of an experience can we get in the same Visual Studio where we’re going to be spending the vast majority of our time anyway?”

Many years ago I wrote several of the Roslyn API “Getting Started” docs. To really get Roslyn you have to have the Roslyn Syntax Visualizer running while you explore, you need to be able to inspect live data while learning. This is the kind of experience I envisioned when we I first wrote them.


I’m definitely not knocking great stuff like Notebooks/Workbooks but I can imagine rich, interactive, workbook-like experiences in VS, using amazing capabilities like inspecting data using custom debugger visualizers that would be very expensive to recreate elsewhere. So, again I’m asking “What if we invert the problem?”

Ultimately there’s a spectrum and this is just one point on it and I respect that we can end up up having more than one solution for different situations. At a minimum top-level code in VB potentially let’s both solutions share a file format. So I’d love to hear from you folks on where you draw your lines.

Let me know your thoughts the feature and this potential application of it. And please share!



P.S. This is Scenario B, it’s the second of 5 (though yesterday I tripped over a 6th). I’m trying a different approach with this prototype than in the past and I’ll post the usual GitHub issue with all the design details/questions I ran into sometime after I post Scenario D.

Top-Level Code Prototype: Scenario A

While most of my prototypes allow you to write more (new) kinds of code this prototype is all about writing less of what’s already in the language. The idea is simple:

  • You should be able to write a complete VB.NET program with exactly ONE line of code; no namespace/class/method declarations.
  • You should be able to write a complete VB.NET program that’s just a sequence of executable statements in a single file, if that’s all you need.
  • You should be able to declare methods and classes as needed in these simple programs in the same files without giving up the lightweight/low-ceremony syntax.
  • This syntax should integrate seamlessly with the rest of the VB.NET language rather than being an alternate dialect/project type/compilation-mode.

Here’s a quick 3 and a half-minute video showing off those ideas in action:

There are benefits for VB coders at every level of experience from first-time programmers to veteran developers:

Simple programs for learning concepts and trying out ideas

A program that’s only one statement is not usually* very useful outside of the fact that for a newcomer to the language to conveys in the most succinct way the smallest (often fundamental) useful thing you can do in that language. This is why “Hello, World!” programs matter. 20 years ago when I discovered QBasic on my Windows 95 upgrade CD I didn’t know how to do any programming at all. Then I read a tutorial and in one line I knew how to display output to the user (myself). Before that line I was not a programmer and couldn’t make a computer do anything that wasn’t already installed and after that line I was a programmer because I could make my computer do that one thing.

I didn’t know anything else but I knew that one thing. And then I learned a second thing, and a third thing, but the journey of a million miles that led me to being a language designer at Microsoft started with one step and ONE concept. Not “This is an import statement, let me explain what a namespace is, here’s a class/module, don’t worry about it, oh yeah, subs, can only write code that runs in here, entry points, gotta be called Main, now let me tell you about Console.WriteLine”.

VB lost that simplicity in the jump from QuickBasic, though it has its own flavor of “Hello, World” in the Command_Click handler demo but for learning language/programming fundamentals outside of GUIs the simplicity of a bunch of statements in a single file can’t be beaten. I can’t tell you the dozens of console applications I spin up and throw away during the course of working on a project to try out ideas, approaches, algorithms, etc.

Scripts (maybe?)

And by this I don’t mean scripting engines (a la VBA) but scripts a la VBScript, PowerShell, Batch files, etc. Even if you’re not learning but just automating some task it’s convenient to be able to do so with as little ceremony as possible. But if the language supports such simple programs natively it could be as straightforward as compile-and-run. It doesn’t even have to be interpreted.

I have little VB.NET programs I’ve written over the years for all sorts of tedious little chores on my computer like re-naming and/or re-tagging all my newly downloaded MP3s that could just as easily be ‘scripts’.

There are upsides, downsides, and concerns to this, of course (like security), but clearly shell scripting languages haven’t stopped being a thing so I’d love to hear your thoughts on whether you’d prefer scripting in VB.NET to PowerShell or Bash.

Better documentation and tools for everyone

Whenever I write a blog post or an article or documentation for something there’s always a tension between writing “all the code you need to copy, paste, & run this as a complete example” and “just the code I’m talking about”. If you look at my “Exhausting List of Differences” post you’ll see that I usually opted for the former. A lot of those code snippets have Module Program : Sub Main : End Sub : End Module boiler plate in them because I wanted it to be fairly easy (and consistent) to just paste them in your IDE and go. It’s less of a win if it’s just a few statements to drop in your Main method but once you start having helper functions you’re calling or data classes you’re referencing it becomes a hassle to “copy this part at the class level, and this part inside the Main method” and with a feature like this all of that goes away in my documentation.

If you look at the docs for VB String Interpolation, for example, you can see the doc writers balancing these same concerns.

And to take it to the next step you have a tool like which aims to let you run code right on a documentation web page. Other great examples include (previously known as TryRoslyn), .NET Fiddle, and more.

I’ve seen the same issue multiple times throughout the years. I’ve seen more than one developer set out to make a IRC bot that will run some VB code but on the back end they have to do some transformation to make the code legally compile-able. That interaction becomes much simpler if the language itself naturally supports simple programs. A feature like this removes that entire step if you can literally hand those statements wholesale to the compiler like a black box and just run its output and send back the result (modulo all the security issues with doing that).


Top-level executable statements and declarations in the vanilla VB.NET language is great for documentation, education, and sets up an easier play for things like shell scripting and tools like Try .NET, SharpLab, .NET Fiddle, and the like.

This is Scenario A, it’s 1 of 5 that I’ve worked out. That’s right there are 4 more demo/posts on this one prototype and they’re different from this one so you’re going to want to pay attention.

I’m trying a different approach with this prototype than in the past because my first video was over 15 minutes and my last video was over 20 minutes long and I think it’s better both for you all and for me to break these prototype-focused posts up. As my friend Jon might say, “Ain’t nobody got time to watch a 30-minute video someone shared on Twitter” and the whole reason I make these videos is so they can be shared and viewed. It also lets me post a little more frequently and longer on a single topic without “going dark” as long to work on the next big project/video and requires a lot less editing.

Let me know your thoughts on this approach and of course on the feature itself. And, as always, Please Share!



JSON Aficionados Wanted!

Do you spend a lot of your time either performing CRUD operations with JSON data?

Is your life an endless stream of calls to an infinite and ever growing set of end-points with unstructured amorphous blobs of key-value pairs that you have to hack into your .NET code using various techniques and workarounds?

If so, tell me if this would help:

In the video I show how a hypothetical JSON literal feature in Visual Basic (inspired by our beloved XML literals) would let you copy & paste JSON from docs and other examples, transform and customize it to your needs, and send it off to a REST service in mere minutes.

Inspect JSON easily with the ! operator

Transform JSON with embedded expressions

Accelerate using REST services by tweaking JSON straight from the docs  

VB enthusiasts have asked about this for years, and I’d really like to advance the discussion on this topic from “Wouldn’t it be cool?” to “That would change my life!” and to do that the community needs to hear from developers who are up to their keyboards in JSON all day. That could be:

  • JSON accepting/returning web services
  • JSON files produced/read by other programs
  • JSON data stored or queried from SQL Server or another database system
  • Transmitting between server code and client-side JavaScript to create dynamic web pages
  • Or something else entirely that I haven’t thought of!

If you’re someone who is about that life or even just considering it please sound off below with your thoughts. And if you’re interested in more details check out the proposal on GitHub and leave feedback there as well and as always SHARE, SHARE, SHARE!



P.S. This is my second language feature prototype and I’m trying to take heart feedback from my first. Feel free to give me more feedback on the format itself and how I can improve it to reach more new and existing VB users. Thanks!

(Maybe) Enhancing `For Each` loops with LINQ


Ever been writing a For Each loop and wanted to tack on a Where, Skip, or Order By clause? Well check out this video of a prototype I wrote that lets you do just that:

But don’t just look at the video! Share! Share this post with any VB developer you can reach who might get a kick out of it. Then go to this GitHub issue and tell everybody what you think. And if you’re really excited, download the fork and play with the prototype yourself!

Why socializing prototypes matters!

Because no matter how brilliant any language designer or team of language designers is they are limited by their own experiences and habits. The most meticulously crafted designs can and have fallen apart when picked up by someone a world away with a slightly different imagination. In the best case, that feature is cut. In the worst case, the wrong feature ships. More eyes looking at it and more hands using it in practical contexts (not clinical samples) for awhile is how you go from an idea that’s cool, to a design that works, to a feature that is actually used.

I’d originally wrote about two more pages of personal experiences from my time on Roslyn of near-misses but I’ve decided to spare you all that. Just trust me—this is crazy important! I’m going to keep looking for more ways to make it easier for as many VB coders as possible to give feedback on ideas in the future, but publicly accessible prototypes and the democratizing power of YouTube is a great first step! Look forward to more videos of other prototypes in the very near future.



P.S. This is my first YouTube video EVER. I know, it sucks, I did everything wrong. That’s OK, I’ll get better. Feel free to leave me feedback about it!

An Exhausting List of Differences Between VB.NET & C#

For almost half of my lifetime now I’ve witnessed or engaged in countless discussions about how similar or different the two most popular .NET languages are. First as a hobbyist, then as a professional, and finally as a customer advocate, program manager, and language designer I cannot exaggerate the number of times I’ve heard or read some variation of:

“…VB.NET is really just a thin skin on top of IL, like C#…”


“…VB.NET is really just C# without semicolons…”

As though the languages were an XML transform or a style sheet.

And when some impassioned passer-by isn’t saying it on a blog comment it’s just as often implied by the premise of a question like:

“Hey, Anthony! I ran into this one little difference in this one corner–is this a bug? How could these two otherwise identical languages that should by all that is good and holy in the world be identical have diverged in this one place. Why has this injustice been visited upon us!?”

diverged” like once they were the same until a mutation occurred and then they were separate species. LOL

And I get it. Before I joined Microsoft I might have vaguely held that idea too and used it in arguments to push back against detractors or reassure someone. I understand its allure. It’s easy to grasp and very easy to repeat. But, working on Roslyn–the complete from-the-ground-up rewrite of both VB and C#–for 5 years, I came to understand how unequivocally false this idea really is. I worked with a team of developers and testers to re-implement from scratch every inch of both languages as well as their tooling in a huge multi-project solution with millions of lines of code written in both. And with so many developers switching back and forth between them, and a high bar for compatibility with the outputs and experiences of previous versions, and the need to faithfully represent every detailed nuance throughout a massive API surface area I got to be intimately familiar with the differences. In fact, at times it felt like I learned something new about my VB.NET (my favorite language) every day.

Well, finally, I’ve made the time to sit down and brain-dump a fraction of what I’ve learned using and creating VB.NET for the last 15 years, in the hopes that I can at least save myself time next time it comes up.

Before I get to the list I’ll lay down the ground rules:

  • This list is not exhaustive. It’s exhausting. It’s not all the differences there are, it’s not even all the differences I’ve ever known, it’s just the differences I can remember off the top of my head until such time as I become too tired to go on; until I’m exhausted. If I or others run into or remember other differences I’ll gladly update this list after the fact.
  • I will start at the top of the VB 11 Language Specification and work my way down, using its content to remind me of my top-of-mind differences on that topic.
  • This is NOT a list of features in VB that aren’t in C#. So no “XML Literals vs Pointers”. That’s fairly low-hanging fruit and there’s tons of lists like that online already (some of which were written by me and maybe I’ll write another one in the future). I’ll be focusing primarily on constructs where there’s an analog in both languages where an uninformed observer might assume those two things behave the same but where there are either subtle or grand differences; these may look the same but they don’t work the same or generate the same code at the end of the day.
  • This is NOT a list of syntax differences between VB and C# (of which there are countless). I’ll mostly be talking about semantic differences (what things mean), rather than syntactic ones (how things are written). So no “VB starts comments with ‘ and C# uses //” or “In C# _ is a legal identifier and it’s not in VB” type stuff. But I will break this rule for a handful of cases; the first section of the spec is about lexical rules, after all.
  • Often enough I’ll provide an example, and occasionally I’ll suggest a rationale for why a design might go one way or the other. Some designs happened on my watch, but the vast majority predate my time and I can only speculate as to why.
  • Please leave a comment or give me a shout on Twitter (@ThatVBGuy) to let me know your favorite differences and/or ones you’d like to dive into further.

Having set those expectations and without further delay…

Table of Contents

Syntax & Pre-processing
1. VB keywords and operators can use full-width characters
2. VB supports “smart-quotes”
3. Pre-processing constants can be of any primitive type (including Dates) and hold any constant value
4. Pre-processing expressions can use arbitrary arithmetic operators

Declarations, etc.
5. VB sometimes omits implements declarations in IL to prevent accidental implicit interface implementation by name
6. VB hides base class members by name by (Shadows) default, rather than by name-and-signature (Overloads)
7. VB11 and under are more protective of Protected members in generics
8. “Named argument” syntax in attributes always initializes properties/fields
9. All top-level declarations are (usually) implicitly in the project root namespace
10. Modules are not emitted as sealed abstract classes in IL so they don’t appear exactly as C# static classes and vice versa.
11. You don’t need an explicit entry-point (Sub Main) method in WinForms apps
12. If you call certain legacy VB runtime methods (e.g. FileOpen, the calling method will implicitly be decorated with an attribute to disable inlining for correctness reasons
13. If your type is decorated with the DesignerGenerated attribute and doesn’t include any explicit constructor declarations, the default one the compiler generates will call InitializeComponent if it’s defined on that type.
14. Absence of the Partial modifier does NOT mean that type is not partial
15. The default accessibility in classes is Public for everything but fields, and Public for fields as well in structures
16. VB initializes fields AFTER the base constructor call whereas C# initializes them BEFORE the base constructor call
17. The implicitly declared backing field of VB events have a different name than they do in C# and are accessible by name
18. The implicitly declared backing field of VB auto-implemented properties have a non-mangled name in VB and are accessible by that name
19. The implicitly declared backing field of VB read-only auto-implemented properties are writeable
20. Attributes on events sometimes apply to the event backing field

21. The scope of a label is the body of the entire method containing it; you can jump into blocks (w/ exceptions)
22. Local variable lifetime <> variable scope
23. Variables are always initialized with the default value for their type
24. RaiseEvent does NOT throw an exception if the event backing field is null
25. Assignments aren’t always the same; sometimes assigning a reference type makes a shallow clone
26. Select Case does not support fall-through; no break required
27. Case blocks each have their own scope
28, 29, 30. Select Case works on non-primitive types, can use arbitrary non-constant expressions in tests, and uses the = operator by default
31. Variables declared inside loops retain their value between iterations, sort of
32. The three For loop expressions are only ever evaluated once at the beginning of the loop
33. For Each loops in VB can use extension method GetEnumerator

34. Boolean conversions
35. Conversions between Enum types, and between Enum types and their underlying types are completely unrestricted, even when Option Strict is On
36. Overflow/underflow checking for integer arithmetic is entirely controlled by the compilation environment (project settings) but VB and C# use different defaults; overflow checking is on by default in VB
37. Conversions from floating-point numbers to integral types using bankers rounding rather than truncating
38. It is not an error to convert NotInheritable classes to and from interfaces they do not implement at compile-time
39. Attempting to unbox a null value to a value type produces the default value of the type rather than a NullReferenceException
40. Unboxing supports primitive conversions
41. There are conversions between String and Char
42. There are conversions between String and Char array
43 & 44. Conversions from String to numeric and Date types support the language literal syntax (mostly)
45. There are NO conversions between Char and integral types

46. Nothing <> null
47. Parentheses affect more than just parsing precedence; they re-classify variables as values
48. Me is always classified as a value—even in structures
49. Extension methods can be accessed by simple name
50. Static imports will not merge method groups
51 & 52. Partial-name qualification & Smart-name resolution
53. Collection initializer Add methods may be extension methods
54. Array creation uses upper-bound rather than size
55. VB array literals are f-ing magic not the same as C# implicitly typed array creation expressions
56. Anonymous type fields can be mutable AND are mutable by default
57. Neither CType nor DirectCast are exactly C# casting
58. The precedence of certain “equivalent” operators is not exactly the same
59. String concatenation is not the same + and & are not the same with regard to String concatenation and + in VB <> + in C#
60. Division works sanely: 3 / 2 = 1.5
61. ^ isn’t exactly Math.Pow
62. Operator =/<> are never reference equality/inequality
63. Operator =/<> on strings are not the same (or any relational operators for that matter)
64. Nullable value types use three-valued logic (null propagation in relational operators)
65. Overloaded operators don’t always have a 1:1 mapping
66. Function() a = b is not the same as () => a = b
67. An Async Function lambda will never be interpreted as an async void lambda
68. Queries are real(er) in VB
69 & 70. As clause in query From doesn’t not always call cast; bonus from ‘As’ clause can perform implicit user-defined conversions
71-75. Select clause isn’t required at all, can appear in the middle of the query, can appear multiple times, and can declare multiple range variables with implicit names, or explicit names

Syntax & Pre-processing

1. VB keywords and operators can use full-width characters

In some languages (I do not know how many), but at least some forms of Chinese, Japanese, and Korean, full-width characters are used. You can read about that in detail here but what it means in short is that when using a fixed-width font (as most programmers do) a Chinese character will take up twice the horizontal space as the Latin characters you’re used to seeing in the west. For example:

I have here a variable declaration written in Japanese and initialized with a string, also written in Japanese. According to Bing translate the variable name is ‘greeting’ and the string says “Hello World!”. The variable name is only 2 characters in Japanese but it occupies the space of 4 of the half-width characters my keyboard normally produces as indicated by the first comment. There are full-width versions of the numbers and all the other printable ASCII characters that occupy the same width as the Japanese characters. I’ve written the second comment using the full-width numbers “1” and “2” to show this. That’s not the same “1” and “2” used in the first comment. There are no spaces between the numbers. You can also see that the characters are not exactly 2 characters wide. There’s some slight offset there. In part this is because that program is mixing full-width and half-width characters in the same line and across all three lines. The spaces are half-width, the alphanumeric characters are full-width. We programmers are nothing if not obsessive about our text alignment so I can imagine that if you’re Chinese, Japanese, or Korean (or anyone else using full-width characters in their language) and using identifiers or strings written natively in those character sets, that these little alignment errors are infuriating.

As I understand it, depending on your Japanese keyboard, it’s easy to switch between kanji and Latin characters but it’s preferable to use full-width Latin characters. VB supports this with the keywords, the spaces, the operators, even the quotes. So this could all be written thus:

As you can see, in this version the keywords, the spaces, the comments, the operators, even the quotation marks are using their full-width versions. Order has been brought to chaos.

Yes. Japanese people use VB. In fact, despite (or maybe because of) the English-like syntax the majority of VB users I see on forums speak a non-English language as their first and I met several Japanese VB MVPs during my time at Microsoft, at least one of whom always brought Japanese candy. If you’re a Chinese, Japanese, or Korean VB programmer (or any other language that uses full-width characters), please leave a comment.

Fun trivia: When I originally implemented interpolated strings in VB I (shamefully) failed to allow for full-width curly braces inside the expression holes. Vladimir Reshetnikov found this bug and implemented the fix so that VBs proud tradition of width-tolerance would remain upheld.

2. VB supports “smart-quotes”

OK, this one is kinda cheap but it’s worth calling out. Have you ever read a code sample in a word document like this:

And then tried to copy the sample into your code only to find that none of the (highlighted) quotation marks work because Word changes all the regular ASCII quotes to “smart-quotes”?

Well, I haven’t. OK, I have, but only when copying C# sample code. In VB smart quotes are legal delimiters for strings:

They also work inside the strings in an arguably wonky way though. If you double-up on the smart quotes to escape them what you get in your string at run-time is just a regular (dumb) quote. It’s a bit odd when you think about it yet practical as all get out since almost nothing would actually expect to see a smart quote in the string. The compiler does NOT enforce that if you start with a smart quote that you end with one or that you use the correct one so you can mix and match however and not get confused. And yes, it also works with the single-quote character used for comments:

I tried to get Paul Vick to confess that he did it precisely because he got tired of this problem when working on the spec but he denies culpability. This didn’t exist in VB6 so someone added it since then.

3. Pre-processing constants can be of any primitive type (including Dates) and hold any constant value

4. Pre-processing expressions can use arbitrary arithmetic operators

Declarations, etc.

5. VB sometimes omits implements declarations in IL to prevent accidental implicit interface implementation by name

This one is pretty esoteric. In VB interface implementation is always explicit. But it turns out that in the absence of an explicit mapping, the CLR’s default behavior is to look for public methods matching an interface member’s name and signature when invoking it. In most cases, this is fine because in VB you’re usually required to provide an implementation for every member in an interface you implement; except in this case:

Interface IFoo
Sub Bar()
Sub Baz()
End Interface
Class Foo
Implements IFoo
Private Sub Bar() Implements IFoo.Bar
Exit Sub
End Sub
Private Sub IFoo_Baz() Implements IFoo.Baz
Exit Sub
End Sub
End Class
Class FooDerived
Inherits Foo
Implements IFoo
Public Sub Bar() Implements IFoo.Bar
Exit Sub
End Sub
Public Sub Baz()
' Does something unrelated to what an IFoo.Baz would do.
End Sub
End Class

In this example the class FooDerived only wants to re-map IFoo.Bar to a new method but leave the other existing mapping untouched. It turns out that if the compiler simply emitted the implements declaration for FooDerived, the CLR would also pick-up FooDerived.Baz as the new implementation of IFoo.Baz (though in this example it’s unrelated). In C# this happens implicitly (and I’m not sure how to opt out of it) but in VB the compiler actually omits the ‘implements’ from the entire declaration to avoid this and only overrides the specific members being re-implemented. In other words, if you ask FooDerived if it implements IFoo directly, it will say ‘no’:

Why do I know this and why is this important? Well, for years VB users have request support for implicit interface implementation (without an explicit Implements clause on every declaration) usually for code-generation scenarios. In part because of this just enabling it with the existing syntax would be a breaking change because FooDerived.Baz would now implicitly implement IFoo.Baz where it didn’t before. But most recently I was educated about this behavior when discussing potential design issues with the default interface implementation feature which would allow interfaces to include default implementations of some members that don’t need to be re-implemented for every class that implements the interface. This would be useful for overloads, for example, where the implementation is highly likely to be the same in all implementers (delegating to the main overload). Another scenario is versioning. If an interface can include default implementations you can add new members to it without breaking older implementers. But here’s where the problem comes in. Because the default behavior in the CLR is to look for public implementations by name and signature, if a VB class didn’t implement the interface members with default implementations but had existing public members with the right name and signature they would implicitly implement those default members, even if doing so was completely unintentional. There are things that can be done to work-around this when the full set of members to the interface is known at compile-time. But in the scenario where a member was added after the code was compiled it would simply silently change behavior at run-time.

6. VB hides base class members by name by (Shadows) default, rather than by name-and-signature (Overloads)

This one is fairly well-known, I think. But the scenario is this, you inherit a base class (DomainObject), possibly outside of your control, and you declare a method with a name that makes sense in the context of your class , e.g. Print:

Class DomainObject
End Class
Class Invoice
Inherits DomainObject
Public Sub Print(copies As Integer)
' Sends contents of invoice to default printer.
End Sub
End Class

It makes sense that an Invoice could be printed. But in a future version of the API the declares your base class decides that for debugging purposes they’re adding a method to all DomainObjects that writes the full contents of that object out to the debug window. This method is brilliantly named Print. The problem is that a consumer of your API could see that Invoice object has Print() and Print(Integer) methods and think they’re related overloads. Perhaps the former just prints 1 copy. But that’s not at all what you, the author of Invoice, intended. You had no idea that DomainObject.Print would come into existence. So, yeah, it doesn’t work like that in VB. There’s a warning when this scenario pops up, but more importantly, the default behavior in VB is hide-by-name. Meaning unless you explicitly state that your Print is an overload of your base class Print via the Overloads keyword, the base class member (and any overloads it may have) are hidden entirely. Only the API you defined originally is presented to consumers of your class. That’s the default but you can make it explicit via the Shadows keyword. C# only has the Overloads capability (thought it will respect it if referencing a VB library) and that is its default (expressed with the new keyword). But this does come up from time to time when certain inheritance hierarchies appear in people’s projects where one class is defined in one language and another in the other and there are methods being overridden but that’s beyond the scope of this bullet point.

7. VB11 and under are more protective of Protected members in generics

We actually changed this between VS2013 and VS2015. Specifically we decided to not bother re-implemented it. But I’m writing it down in case you’re on an older version and notice it. In short, given a Protected member declared in a generic type, a derived type that is also generic may only access that protected member through a derived instance with the same type arguments.

Class Base(Of T)
Protected x As T
End Class
Class Derived(Of T)
Inherits Base(Of T)
Public Sub F(y As Derived(Of String))
' Error: Derived(Of T) cannot access Derived(Of String)'s
' protected members
y.x = "a"
End Sub
End Class

We thought the rule was over-protective (no pun intended) and dropped it in Roslyn.

8. “Named argument” syntax in attributes always initializes properties/fields

VB uses the same syntax := for initializing attribute properties/fields as for passing method arguments by name. Consequently there’s no way to pass an argument to the constructor of an attribute by name.

9. All top-level declarations are (usually) implicitly in the project root namespace

This one is almost in the “extra feature” category, but I include it because it does change the meaning of code. In the VB project properties there is a field:

By default it’s just the name of your project when you create it. It is NOT the same as the box in the C# project properties called “Default namespace”. The default namespace is just what code is spit into new files by default in C#. But VBs root namespace means that, unless otherwise stated, every top-level declaration in this project is implicitly in this namespace. That’s why the VB item templates don’t usually include any namespace declaration. Furthermore, if you do include a namespace declaration it isn’t overriding the root but adding to it:

Namespace Controllers
' Child namespace.
End Namespace
Namespace Global.Controllers
' Top-level namespace
End Namespace

So namespace Controllers is actually declaring a namespace VBExamples.Controllers unless you escape this mechanism by explicitly rooting your namespace declaration in the Global namespace.

This is convenient because it saves every VB file 1 level of indentation and one extra concept. And it’s extra useful if you’re making a UWP app (because everything must be in a namespace in UWP) and extremely convenient if you decide to change the top-level namespace for your entire project, say from some code name like Roslyn to a longer release name like Microsoft.CodeAnalysis so you don’t have to manually update every file in the solution. It’s also important to keep in mind when dealing with code-generators and XAML namespaces and the new .vbproj file format.

10. Modules are not emitted as sealed abstract classes in IL so they don’t appear exactly as C# static classes and vice versa.

VB Modules pre-exist C# static classes, though we did try to make them look the same in IL in 2010. Sadly this was a breaking change because the XML Serializer (or maybe it was the binary one) for that version of .NET (I think they fixed it) won’t serialize a type nested in a type that can’t be constructed (which abstract types can’t be). It throws an exception.

We discovered this after making the change and reverted it because some code somewhere used an enum type which was nested inside a module. And since you can’t know which version of the serializer will be run against a compiled program it can never be changed as it would work in one version of an app and throw exceptions in other ones.

11. You don’t need an explicit entry-point (Sub Main) method in WinForms apps

If your project sets a Form as the start-up object and does not use the “Application Framework” (more on that in a future post) VB will synthesize a Sub Main that instantiates your startup form and passes it to Application.Run, thus saving you a separate file to manage this, or an extra method in your Form, or ever having to think about this problem.

12. If you call certain legacy VB runtime methods (e.g. FileOpen, the calling method will implicitly be decorated with an attribute to disable inlining for correctness reasons

In short, VB6 style file I/O methods like FileOpen rely on tracking state specific to the assembly the code appears. For example file #1 may be a log in one project, and a config file in another; what (1) means is contextual. To determine what assembly is running Assembly.GetCallingAssembly() is called. But if the JIT inlines your method into your caller from the perspective of the stack the VB runtime method is being called by your caller, not your method, which may be in a different assembly, which then could allow your code to access/corrupt private state from your caller. It’s not a security solution because if compromised code is running in process you’ve already lost, but it is a correctness one. So if you’re using these methods the compiler disables inlining.

This was a last minute (and thus stressful) change made in 2010 because the x64 JIT is VERY aggressive at inlining/optimizing code and we discovered it very late in the cycle and this was the safest option.

13. If your type is decorated with the DesignerGenerated attribute and doesn’t include any explicit constructor declarations, the default one the compiler generates will call InitializeComponent if it’s defined on that type.

In the age before Partial types existed, a war was waged by the VB Team to lower the boilerplate in VB WinForms projects. But even with Partial this solves a problem because it allows the generated file to omit the constructor entirely so that a VB user may manually declare one if they needed in their own file or not as needed. Without this the designer would need to add the constructor just to invoke InitializeComponent and if the user added one they’d be duplicates, or the tooling would need to be smart enough to move the constructor over from the designer file to the user file, and to not re-generate it in the designer if it exists in the user file.

14. Absence of the Partial modifier does NOT mean that type is not partial

Technically in VB only one class has to be marked Partial. This is usually (in GUI projects) the generated file.

Why? It keeps the user file nice and clean and can be very convenient for opting into generation after the fact, or augmenting generated code with user code. However, the advice is that at most one class omit the Partial modifier and if more than one definition omits it a warning is reported.

15. The default accessibility in classes is Public for everything but fields, and Public for fields as well in structures

Mixed feelings about this. In C# everything is private by default (Yay, encapsulation!) but there’s an argument to be made based on what you’re more often declaring, your public contract or your implementation details. Properties and events are usually for public consumption and operators can’t be anything but public. I rarely rely on default accessibility myself though (except in demos, like all of the examples on this page).

16. VB initializes fields AFTER the base constructor call whereas C# initializes them BEFORE the base constructor call

You know how “they” say that the first thing that happens in a constructor is a call to the base class constructor? Well, it’s not, at least in C#. In C#, before the call to base(), whether implicit or explicit, the initializers on fields execute first, then the constructor call, then your code. There are implications to this design decision and I think I know why a language designer would go one way or the other. I believe on such implication is that this code can’t be translated into C# directly:

Imports System.Reflection
Class ReflectionFoo
Private StringType As Type = GetType(String)
Private StringLengthProperty As PropertyInfo = StringType.GetProperty("Length")
Private StringGetEnumeratorMethod As MethodInfo = StringType.GetMethod("GetEnumerator")
Private StringEnumeratorType As Type = StringGetEnumeratorMethod.ReturnType
Sub New()
End Sub
End Class

Back in my Reflection-slinging days I used to write code like this a lot. And I vaguely recall a pre-Microsoft co-worker (Josh) who would translate my code to C# sometimes complaining about having to move all my initializer code into the constructor. In C# it’s illegal to reference the object under construction before the base() call. And since field initializers run before said call it’s also illegal for field initializers to refer to other fields, or any instance members of the object, actually. So this example also only works, as written, in VB:

MustInherit Class Base
Private Cached As Object = DerivedFactory()
Protected MustOverride Function DerivedFactory() As Object
End Class
Class Derived
Inherits Base
Protected Overrides Function DerivedFactory() As Object
Return New Object()
End Function
End Class

Here, we have a base class that presumably has a bunch of functionality in it but it needs some key object to manage, operate on, that is specified by a derived type. There are many many ways to write this pattern, but this is the one I usually go for, because:

  • It’s short
  • Doesn’t require me to declare a constructor
  • Doesn’t require me to put initialization code in the constructor if there is one
  • Allows me to cache the created object and doesn’t require derived types to declare and manage storage for the object provided, though now with auto-props that’s far less of an issue.

Now, I’ve been in both situations, where a field in a derived type wanted to call an instance method declared on the base class, and in this situation where a base class field initializer has needed to invoke a MustOverride member filled in by a derived type. Both are legal in VB and neither are in C#, and it kinda makes sense. If a C# field initializer could call a base class member that member might depend on fields initialized in the base constructor–which hasn’t run yet–and the results would almost certainly be wrong and there’s no way around it.

But in VB the base constructor has always already run, so you can go wild! In the opposite situation, it’s a little trickier, because calling an Overridable member in a base class initializer (or constructor) could result in accessing fields before they’re “initialized”. But only your implementation knows whether that’s a problem. In my usages that just doesn’t happen. They don’t depend on instance state but they can’t be Shared members because you can’t have a Shared Overridable member in any language for technical reasons beyond the scope of this blog post. Additionally, it’s well-defined what happens to fields before user-written initializers are run, they’re initialized to their default values, like all variables in VB. There are no surprises here.

So why? I don’t actually know that my scenarios were what the original VB.NET team had in mind when they designed this. It just really worked out for me! I think it’s actually a much simpler reason: The VB design ensures that you can always write in a field initializer what you could write in the constructor. We intuitively think of field initializers as shorthand for writing assignments in the constructor. With this design they are very much that.

Incidentally, this difference is very important to keep in mind when designing auto-prop initializers and primary constructors and an example of why you can’t just copy-paste a C# design on top of VB, especially if that design relies on assumptions about VB imposing the same restrictions as C#.

17. The implicitly declared backing field of VB events have a different name than they do in C# and are accessible by name

This matters maybe for Reflection and serialization (which is really just more Reflection) reasons. Given a simple declaration of an event named E, in VB there’s a (hidden in IDE) field named EEvent declared. In C# the field is also named E and the language has special rules about when the expression E refers to the event and when it refers to the field.

18. The implicitly declared backing field of VB auto-implemented properties have a non-mangled name in VB and are accessible by that name

Given an auto-prop named P, a field is generated named _P‘. It’s hidden in IntelliSense but can be accessed if needed. In C# this field has a “mangled” name, meaning it’s a name that cannot be declared or referenced in C# itself, usually including special characters.

Why? The VB team elected to use a well-known name in the case 1) because it’s consistent with the precedent set by event backing fields and ‘WithEvents’ variables, and 2) so that the name could stay the same if the auto-prop is ever expanded into a normal property, which is important for preserving serialization backward compatibility.

19. The implicitly declared backing field of VB read-only auto-implemented properties are writeable

Some people wanted the fields to also be read-only because … purity. But VB has a strong precedent for “escape hatches” to its magic features. Even though the backing field of a WithEvents variable, non-Custom events, and read-write auto-props are almost never intended to be accessed directly, there’s still a hidden way to bypass the accessors if your scenario requires it. The variables are hidden from IntelliSense so you have to go out of your way but if you need the flexibility it’s there. Philosophical self-consistency FTW! Also, it gives VB a concise feature comparable to what in C# would be declared as a private set; auto-prop.

Class Alarm
Private ReadOnly Code As Integer
ReadOnly Property Status As String = "Disarmed"
Sub New(code As Integer)
Me.Code = code
End Sub
Sub Arm()
' I'm motifying the value of this externally read-only property here.
_Status = "Armed"
End Sub
Function Disarm(code As Integer) As Boolean
If code = Me.Code Then
' And here.
_Status = "Disarmed"
Return True
Return False
End If
End Function
End Class

20. Attributes on events sometimes apply to the event backing field

Specifically the NonSerialized attribute.

Because VB didn’t get a syntax for declaring an expanded Custom event until 2005 (?) and doesn’t have an attribute target syntax for type members, it was impossible to explicitly declare the backing field for an event and thus apply the NonSerialized attribute. This is something you’d absolutely want to do because objects listening to your events aren’t really part of “your” state-proper and shouldn’t be part of what’s considered your “data contract”.

This really bit some people hard who would serialize objects because the serializer would attempt to serialize the event backing field, and thus all the listeners to the event. So if, for example, you had a data class that was also two-way data-bindable (and thus declared a PropertyChanged event) the serializer would try to serialize any controls bound to that object, and of course fail.

And example of this near and dear to my heart is from earlier versions of Rocky Lhotka’s CLSA “Expert Business Objects” framework, which would use serialization to provide undo/redo capabilities–it would serialize a copy of what the object looked like before when you changed something and deserialize if you undid the changes–as well as object cloning, and network marshalling. So adding this special-case really unblocked impacted customers. Plus, it’s pretty neat not having to completely re-implement a full event manually just to opt out of serialization.


21. The scope of a label is the body of the entire method containing it; you can jump into blocks (w/ exceptions)

That’s right, you can GoTo a label inside a block from outside of that block. There are some restrictions, usually when doing so would by-pass some important language construct initialization, e.g. you can’t jump into a For or For Each loop; Using, SyncLock, or With block, and I think certain cases involving lambda captures and Finally blocks. But If and Select Case blocks, Do and While loops, and even Try blocks are fair game and I’ve encountered scenarios for all of them:

Module Program
Sub Main()
Dim retryCount = 0
' IO call.
Catch ex As IO.IOException When retryCount < 3
retryCount += 1
GoTo Retry
End Try
End Sub
End Module

The reason for this is very likely the fact that prior to .NET VB didn’t have “block” scoping of anything. In VB6 all the way back to my Quick Basic days, labels (and variables) were scoped to the entire containing method. When I started coding in QB, indentation was a stylistic suggestion. It made the code read better but it wasn’t as much a reflection of the structure of “scopes” and often enough all my code was left-aligned. Also, if you’re going to be using GoTo it’s unlikely that block scoping is your highest order bit, in fact it would probably defeat the purpose.

Heads up: This Try scenario is also important to keep in mind if VB ever supports Await in Catch and Finally blocks since the code generated if there is such a GoTo in the Catch would need to be a little different.

22. Local variable lifetime <> variable scope

As a follow-up to the previous point, in VB a (non-Static) local variable’s lifetime (how long that variable has a value in it) isn’t the same as its scope (where it can be referenced by name). And it makes sense, especially considering the previous point. In my example, execution would leave the Catch block on an exception and retry up to 3 times. Even though any variables inside the Try block are out of scope inside the Catch and can’t be referenced, it’s both reasonable and necessary that upon execution re-entering the Try block that those variables still have their previous values.

Again, prior to VB.NET, variables were scoped to the method so it didn’t really matter. But even aside from that at the CLR level this is true and even in the absence of VBs ability to jump into blocks. It’s also consistent with the debugging experience: if, while debugging, the developer moves the instruction pointer back into a block that had been exited previously.

Technically, C# specifies that the actual lifetime of a variable is implementation-dependent so the behavior under the debugger isn’t “wrong”. It’s just that in VB.NET the actual lifetime is far more observable.

23. Variables are always initialized with the default value for their type

I wasn’t going to call this out at first, but it comes up a lot in language design discussions, because C# has these hardcore set of rules about “definite assignment”. The idea is that the language needs rules to ensure that you never accidentally access “uninitialized memory”. This is actually dangerous, if leftover (or code) in memory from some previous usage is now loaded into a pointer variable which is accidentally dereferenced and now your app crashes or computer blue screens. This is a piece of that C/C++ legacy. Because C is all about that performance, baby! Every operation is precious and any CPU time being spent needs to be explicit. So automatically zero-ing out memory before code uses it for safety purposes is right out. If the user desperately wants to not access garbage data they should write it out explicitly so that it’s explicit that they’re asking for a paying for those CPU cycles and so that, in the event that they’ve written a perfectly optimized algorithm that initializes that variable with some non-zero value anyway they’re not paying for both the zero-init and the explicit init. But yeah, BASIC languages don’t feel that way so all our variables are auto-initialized to their default value and there’s no way to access “random” memory so every variable doesn’t need = Nothing, = 0, = False, etc.

Consequently, VB flow analysis is more like guidelines than actual rules.

Definite assignment rules impose a hefty tax on language design as well because the C# rules have to be kept air-tight to guarantee you never access a variable at a location where it has not been definitely assigned on every path to that location. VB has warnings in some situations like this but they were originally specifically targeted at helping developers find potential sources of null references, not at encouraging more redundant initializers. In Roslyn, however, the APIs still use a more rigorous definition of “definitely assigned” so that the refactoring experience is at its best, though technically variables are always definitely assigned.

24. RaiseEvent does NOT throw an exception if the event backing field is null

I’ve seen this a few times in the past when someone tried to translate some C# into VB. VBs ‘RaiseEvent’ isn’t just a translation of directly invoking the backing field, it actually checks for null (in a thread-safe way) so the null handler scenario isn’t one you ever have to think about.

' You don't have to write this:
If PropertyChangedEvent IsNot Nothing Then
RaiseEvent PropertyChanged(Me, e)
End If
' You don't have to write this:
Dim handlers = PropertyChangedEvent
If handlers IsNot Nothing Then
handlers(Me, e)
End If
' You don't have to write this either:
PropertyChangedEvent?(Me, e)
' Just write this:
RaiseEvent PropertyChanged(Me, e)

Consequently, while using the null-conditional invocation syntax in C# as of VS2015 is a big win for C# in this scenario, it’s a much smaller win for VB (still a win though) and I would not advise anyone start going out of their way to use it needlessly (usually); idiomatic VB.NET code will continue to serve you well.

25. Assignments aren’t always the same; sometimes assigning a reference type makes a shallow clone

Here’s one of those differences that if you haven’t noticed it in the last 17 years, it probably doesn’t matter. When you assign a boxed value type to assignee of type Object, the compiler injects a call to a method called System.Runtime.CompilerServices.RuntimeHelper.GetObjectValue. This is a special method implemented internally inside the CLR. What it does is, given a reference to an object:

  • If the object is a reference type, it returns that reference unchanged.
  • If the object is a boxed value type that’s known to be immutable (e.g. all primitive value types like Integer), it returns that reference unchanged.
  • If the object is any other boxed value type, it copies the value into a new boxed value and returns a reference to that.

The reason this is done is to preserve value type semantics, that is to say that values are always copied, even in late-bound situations. So even if I have a boxed mutable structure and I pass it, still boxed, to a method, and that method performs late-bound operations on the boxed object that change it, it’s still only operating on a copy of the value, not the caller’s copy. So if you’re code is weakly typed and all dynamic, or strongly-typed and early bound, or somewhere in the middle the semantics of value-types stays the same.

I ran into this exactly once in my career. It was a program like this:

Class MyEventArgs
Property Value As Object
End Class
Structure MyStruct
Public X, Y As Integer
End Structure
Module Program
Sub Main()
Dim defaultValue As Object = New MyStruct With {.X = 3, .Y = 5}
Dim e = New MyEventArgs With {.Value = defaultValue}
RaiseEvent DoSomething(Nothing, e)
If e.Value Is defaultValue Then
' No handlers have changed anything.
End If
End Sub
Event DoSomething(sender As Object, e As MyEventArgs)
End Module

There was some sort of event pipeline, similar to a WPF value converter, where the code would start out with a default value and give other code the opportunity to modify that value. If nothing was changed then the code would take a fast path. Logically, if I started out with a boxed value and the event args referenced the same boxed object after raising the event then none of the handlers had updated the value. But I soon learned that that was never the case. I don’t think I was ever able to workaround this behavior so I probably gave up on using a boxed value type and changed my default value to a class.

Incidentally, the documentation for the helper indicates that other “dynamic languages” may also make use of this helper to preserve value semantics. I didn’t check IronRuby/Python, but I did check C#’s dynamic (and the C# compiler) and C# doesn’t inject calls to GetObjectValue on assignment between locations typed as dynamic. But, funny enough my first instinct when checking this was to use object.ReferenceEquals to see if the references were the same and that did copy the boxed value, somewhere, deep in the bowels of C# dynamic callsites (because it was a dynamic call). But when I switched to using object == it didn’t. So C# sometimes at least shares this goal of preserving value semantics in late-bound situations.

26. Select Case does not support fall-through; no break required

In the code below, Friday is the only weekday and Sunday is the only weekend day, and the other 5 days of the week are lost.

Module Program
Sub Main()
Select Case Today.DayOfWeek
Case DayOfWeek.Monday:
Case DayOfWeek.Tuesday:
Case DayOfWeek.Wednesday:
Case DayOfWeek.Thursday:
Case DayOfWeek.Friday:
Case DayOfWeek.Saturday:
Case DayOfWeek.Sunday:
End Select
End Sub
End Module

One day a developer on the Roslyn team from the C# side calls me over, pulls some code up on his laptop, and he says, “You know what I learned today? That doesn’t fall through!” and I say “No, it doesn’t” and there was much laughter. VS actually removes those colons if you type them, but it just so happened that the code was generated and no one reviewed the generated code, it just didn’t work right. We fixed it, though!

So these are different for a classic reason. C# is designed to be familiar to developers from the C-family of languages and that’s how C switches work. They fall through from one case to another. Incidentally, C# doesn’t technically support fall-through either, unless the case section is completely empty. If you put anything in there you either need an explicit goto or a break. There is an equivalent of break in this context in VB, Exit Select but it’s not needed at the end of a block because there’s no fall-through at all in VB.

27. Case blocks each have their own scope

This one was a surprise to me, the first time it came up. But if you were to write the equivalent code to this in C# you’d get an error:

Module Program
Sub Main()
Select Case Today.DayOfWeek
Case DayOfWeek.Monday,
Dim message = "Get to work!"
Case DayOfWeek.Saturday,
Dim message = "Funtime!"
End Select
End Sub
End Module

The error would say that message is already declared and can’t be declared twice, because in C# the entire switch statement is a single scope and each case label is really just that, a label–they don’t declare scopes. Which kinda makes sense (in C at least), if you’re falling through from one section to the next there may be state than needs to be shared between sections, I guess.

28, 29, 30. Select Case works on non-primitive types, can use arbitrary non-constant expressions in tests, and uses the = operator by default

So here’s a thing I just never knew until a language design meeting started to pivot on faulty assumptions about what existing code could exist today using Select Case and how potential new features would need to design around it.

I’ve got a great example of this in mind for a follow-up post but for now I’ll talk about the philosophical reasons these things are different. It boils down to this:

  • Select Case is shorthand for when you want to test the same value multiple times but…
    • don’t want to repeat the value (or its side-effects) multiple times across an If/ElseIf/Else block and/or…
    • don’t want to store the value in a variable in order to perform multiple tests
  • switch is a fast opcode/native instruction known as a “jump- or branch-table

That’s the difference that sums up differences 26-30. switch in the past has constrained itself to scenarios where the performance of the code generated by the compiler is faster than multiple successive if tests. There is an actual IL instruction switch which is much more efficient than multiple If tests and the VB compiler will use it under the circumstances that it is faster, as an optimization. But philosophically the switch in the past has been limited to such scenarios, I believe, as an inheritor of C’s belief in performance transparency. In VB it’s purely about convenience of expressing yourself.

31. Variables declared inside loops retain their value between iterations, sort of

In this example, every iteration x has the same value it ended the previous iteration with so the numbers -1, -2, -3 are written to the console:

Module Program
Sub Main()
For i = 1 To 3
Dim x As Integer
x -= 1
End Sub
End Module

Technically each iteration “a fresh copy is made of all local variables declared in that body, initialized to the previous values of the variables” (per the spec). It’s a subtle difference that only became observable in VB2008 when lambda expressions were introduced and later:

Module Program
Sub Main()
Dim lambdas = New List(Of Action)
For i = 1 To 3
Dim x As Integer
x -= 1
lambdas.Add(Sub() Console.WriteLine(x))
For Each lambda In lambdas
End Sub
End Module

This example also prints out -1, -2, -3. Because technically each x is a “fresh copy”, the lambda expression captures the value of x for that iteration only, which is most often what you want. But you still get to carry the value over from previous iterations as though it were a single variable x for the life of the loop. Try representing that in a flow analysis API – I dare you! (“The… variable… assigned to… itself?”)

Why? I asked around and those who were on the team the longest couldn’t remember exactly, but thinking back on it it makes a lot of since combined with the rationale for #22. It’s the best way to introduce block-level scoping while preserving the behavior of method-level variable lifetime, and once you add lambdas to the mix copying the variable is the only design that is practical and intuitive.

Incidentally, the VB and C# design teams decided to change the behavior of For Each control variables in VS2012(?) so that lambda expressions would capture them ‘per iteration’. This is 10000% more practical and intuitive than what the behavior was before (in fact, VB reported a warning before in this scenario because the behavior was so un-intuitive). Additionally, the VB language design team very carefully considered changing the behavior of For control variables to be like variables inside the loop so that you could still change its value inside the loop but that once captured the current value would freeze like this. That change was considered with the idea that VB For loops were a lot closer to VB For Each loops than C# for and foreach loops were to each other. Ultimately we never made that change but the VB For loop still reports a warning if the control variable is captured because the behavior is often surprising.

32. The three For loop expressions are only ever evaluated once at the beginning of the loop

Once started you can’t change the bounds of a For loop. The expressions in the loop header are only evaluated once and the results cached, so this example will print 1,3,5,7,9 even though changing the upper-bound and increment might make you think it’ll go on forever.

Module Program
Sub Main()
Dim lower = 1,
upper = 9,
increment = 2
For i = lower To upper Step increment
upper += 1
increment -= 1
End Sub
End Module

This example will print 1,3,5,7,9 even though changing the upper-bound and increment might make you think it’ll go on forever.

This is an optimization if those expressions are complex and have enough side-effects–the size of an array never changes after all–but it does mean that if you modify a collection you’re iterating over you won’t iterate over new elements and you might get IndexOutOfRangeExceptions trying to index removed elements.

That said, I’m not sure the world would work without this if you consider that unlike the C-style for loop the condition of the VB For loop is inferred. Have you ever considered how VB knows whether a loop For i = a To b Step c is counting up (and should stop once i > b) or counting down (and should stop once i < b), particularly if c isn’t known at compile-time? It’s all quite exciting reading, especially in the late-bound case, but that house of cards would come tumbling down pretty hard if b were sometimes positive and sometimes negative. I’m not even sure what the expectations should be so thankfully I’ll never have to think about it.

33. For Each loops in VB can use extension method GetEnumerator

In order for be For Each-able a type doesn’t need to implement IEnumerable, the compiler just needs to be able to find a method called GetEnumerator off of the collection being For Each-ed over. For example, I’ve always thought that you should be able to For Each over an IEnumerator in situations where you’ve consumed part of it already and want to resume iteration, e.g.:

Module Program
Sub Main()
Dim list = New List(Of Integer) From {1, 2, 3, 4, 5}
Dim info = list.FirstAndRest()
If info.First IsNot Nothing Then
For Each other In info.Additional
Console.Write(", ")
End If
End Sub
Function FirstAndRest(Of T As Structure)(sequence As IEnumerable(Of T)) As (First As T?, Additional As IEnumerator(Of T))
Dim enumerator = sequence.GetEnumerator()
If enumerator.MoveNext() Then
Return (enumerator.Current, enumerator)
Return (Nothing, enumerator)
End If
End Function
Function GetEnumerator(Of T)(enumerator As IEnumerator(Of T)) As IEnumerator(Of T)
Return enumerator
End Function
End Module

In this example, I’ve taken a queue from my F# friends and split the sequence into the first element and the rest and extended IEnumerator so that I can For Each over any unconsumed items remaining in the sequence.

There’s a general theme in VB that when a language construct needs to find a member with a well-known name that that member can be an extension method. This also applies to, for example, the Add method used by collection initializers. C# by default does not do this but they’ve been getting more relaxed about it (e.g. async/await) with each version. In fact, there was a bug where the Roslyn C# compiler did (accidentally) for collection initializers and they decided to keep it.


34. Boolean conversions

Converting Boolean True to any signed numeric type produces -1, to any unsigned numeric the max value for that numeric, whereas in C# such conversions don’t exist. However, the Convert.ToInt32 method, for example, converts True to 1 and that’s how it’s most often represented in IL. Going in the other direction any non-0 number will convert to True.

Why? The reason VB prefers to use -1 to 1 is because the bitwise negation of 0 (all bits set to 0) in any language is -1 (all bits set to 1) so using this value unifies logical and bitwise operations such as And, Or, and Xor.

Conversions to and from the strings “True” and “False” (case-insensitive of course) are also supported.

35. Conversions between Enum types, and between Enum types and their underlying types are completely unrestricted, even when Option Strict is On

Philosophically the language treats Enum types more as a collection of named constants of their underlying integral type. The place where this is most apparent is equality. It’s always legal to compare any integer to an enum value whereas in C# this gives an error.

Story time: The Roslyn API went through many internal revisions. But in all of them each language had a dedicated enumeration called SyntaxKind which would tell you what kind of syntactic construct a node represented (e.g. IfStatement, TryCastExpression). One day a developer was using an API that attempted to abstract over language and returned one of these SyntaxKind values but only as an Integer and after not getting an error when comparing a raw Integer to SyntaxKind promptly came to my office to complain “That it’s really an int is an implementation detail, I should be forced to cast!”.

Years later during another revision of the API we removed the properties (Property Kind As SyntaxKind) that told you the language specific kind entirely and all the APIs started returning Integers. All the C# code broke and all the VB code continued worked as though nothing happened.

A bit after that we decided to rename that property to RawKind and add extension language-specific methods called Kind(). The all the C# code broke because parentheses were required for method invocations but since they aren’t in VB, once again all the VB code continued working as though nothing happened.

36. Overflow/underflow checking for integer arithmetic is entirely controlled by the compilation environment (project settings) but VB and C# use different defaults; overflow checking is on by default in VB

Integral types each have a range, so for example Byte can represent values 0 to 255. So what happens when you add Byte 1 to Byte 255? If overflow/underflow checking is turned off the value wraps around to 0. If the type is signed it wraps all the way around to the lowest negative number (e.g. -128 for SByte). This likely indicates an error in your program. If overflow/underflow checking is on an exception is thrown. To see what I mean, take a look at this harmless For loop.

Module Program
Sub Main()
For i As Byte = 0 To 255
End Sub
End Module

By default, in VB this loop will throw an exception (because the last iteration of the loop increments beyond the bounds of a Byte. But with overflow checking off it loops indefinitely because after 255 i becomes 0 again.

Underflow is the opposite situation where subtracting below the minimum value for a type wraps around to the max value.

A more common situation for overflow is just adding two numbers. Again, take the numbers 130 and 150, both as Byte. If you add them the answer is 280, which can’t fit into a Byte. But your CPU doesn’t see it that way. Instead it says the answer is 24.

This has nothing to do with conversions, by the way. Adding two bytes produces a byte; it’s just the way binary math works. Though you can also get an overflow by performing a conversion when you try to convert a Long to an Integer for example. Without overflow checking the program just chops off the extra bits and stuffs as many will fit into that variable.

Why the difference? Performance. The CLR checking for overflow costs a tiny bit of computing time versus doing it the unchecked way as do all safety checks. VB comes from a philosophy that developer productivity trumps performance so you’re opted into the safety check by default. The C# design team might make a different decision on project defaults today but if you consider that the first C# developers were converted C/C++ developers that group of people would probably demand that the code not silently do any extra stuff that would cost CPU cycles; it’s a hard philosophical difference.

Trivia: Even if overflow/underflow checking is off, converting a Single or Double value of PositiveInfinity, NegativeInfinity, or NaN to Decimal will throw an exception as none of those values are represent-able in the Decimal type at all.

37. Conversions from floating-point numbers to integral types using bankers rounding rather than truncating

In VB, if you convert the number 1.7 to an integer, the result will be 2. In C#, the result will be 1. I can’t speak to mathematical norms outside of America but my instinct when going from a real number to a whole number is to round. And nobody I’ve ever met outside of a programmer thinks that the closest whole number to 1.7 is 1.

There are actually several ways one can round and the type of rounding VB (and .NET’s Math.Round method) uses by default is called bankers’ or statisticians’ rounding. Which means that for a number halfway between two whole numbers VB rounds to the nearest even number. So 1.5 rounds to 2 and 4.5 rounds to 4. Which actually isn’t how we’re taught to round in school–I was taught the round up from .5 (technically to round away from zero). But as the name suggests, Bankers rounding has the benefit that over a large set of calculations you break even on rounding as opposed to always giving away money or always keeping the extra. In other words, it limits the skewing of data over a large set by limited statistical bias.

Why the difference? Rounding is more intuitive and practical, truncating is faster. If you consider the use of VB in line of business applications and especially in applications like Excel macros running VBA, just willy nilly throwing away the decimal digits all the time would cause… problems.

I think there’s a case to be made that how one converts is truly ambiguous and should always have to be explicit, but if you’re going to pick one…

38. It is not an error to convert NotInheritable classes to and from interfaces they do not implement at compile-time

Generally speaking if you test a class that’s NotInheritable for an interface you can know at compile-time whether such a conversion is possible because you know all the interfaces that type and all of its base types implement. You can’t know that such a conversion isn’t possible if the type is inheritable because the runtime type of the object being referenced might actually be of a more derived type that does implement that interface. However, there is an exception to this due to COM interop where at compile-time it may not appear that a type has any relation to an interface but at runtime it does. For this reason the VB compiler instead reports a warning in such cases.

Why? VB and COM grew up together, back when they were kids in the old neighborhood. So there are several design decisions in the language where VB has a stronger consideration for things that existed only in COM at the time .NET 1.0 shipped.

39. Attempting to unbox a null value to a value type produces the default value of the type rather than a NullReferenceException

I suppose technically that’s also true for reference types, but, yes:

  • CInt(CObj(Nothing)) = 0.

Why? Because CInt(Nothing) = 0 and the language aims to be somewhat consistent whether you typed your variables or not. This applies to any structure, not just the built-in value types. See rationale for #25 for more on this.

40. Unboxing supports primitive conversions

In both VB and C# you can convert a Short into an Integer but what if you try to convert a boxed Short to an Integer? In VB the Short will first be unboxed, then converted to an Integer. In C# an InvalidCastException will be thrown unless you manually unbox the short then convert to int.

This applies to all of the intrinsic conversions, so boxed numerics, conversions between strings and numerics, strings and dates (yes, Decimal and Date are primitive types).

Why? Again, to give consistent behavior whether your program is entirely strongly-typed, or typed as Object, or in the middle of a refactoring from one to the other. See #39 above.

41. There are conversions between String and Char

  • A String converts to a Char representing its first character.
  • A Char converts to a String the only way that could make sense.

Because no one but me remembers the syntax for a char literal in VB (nor should they have to).

42. There are conversions between String and Char array

  • A String converts to a Char array of all its characters.
  • A Char array converts to a String of all its elements.

To be clear, these conversions produce new objects, you don’t get access to the internal structure of a String.

Fun story: I was once found (or maybe it was reported and I investigated) a breaking change between .NET 3.5 and 4.0 because in between those versions the .NET team added the ParamArray modifier to the second parameter of the overload of String.Join that takes a String array. The precise setup for this is lost to time (probably for the best) but I believe the reason was that with the ParamArray modifier it was now acceptable to convert a Char array into a string and pass that as the single element to the param array. Fun stuff.

43 & 44. Conversions from String to numeric and Date types support the language literal syntax (mostly)

  • CInt("&HFF") = 255
  • CInt("1e6") = 1_000_000
  • CDate("#12/31/1999#") = #12/31/1999#

This works with the base prefixes and so makes for a very convenient way to convert hexadecimal (or octal) input into a number: CInt("&H" & input). Sadly, this symmetry is bit-rotting at the time of this writing because the VB runtime hasn’t been updated to support the binary prefix &B or the digit group separator 1_000 but I’m hoping this will be fixed in a future version. Scientific notation works but not type suffixes and date conversions also support standard date formats so the ISO-8601 format JSON uses also works: CDate("2012-03-19T07:22Z") = #3/19/2012 02:22:00 AM#.

Why? Other than being convenient I don’t know why this works. But I have been very tempted to propose also supporting other common formats that are near-ubiquitous on the web today like #FF, U+FF, 0xFF. I imagine it could really grease the wheels for some kinds of applications…

45. There are NO conversions between Char and integral types


After reading about all those extra conversions are you surprised? It is illegal in VB to attempt to convert between Char and Integer:

  • CInt("A"c) does not compile.
  • CChar(1) does not compile.

Why? It’s ambiguous what should happen. Usually VB takes a pragmatic and/or intuitive approach in these situations but given expressions CInt("1"c) I think half of readers would expect the number 1 (the value of the character 1) and half would expect the number 49 (the ASCII/UTF code for the character 1). Rather than pick wrong half the time VB has dedicated conversion functions for converting characters to and from their ASCII/Unicode codes, AscW and ChrW respectively.


46. Nothing <> null

The literal Nothing in VB does not mean “a null value”. It means “the default value of the type it’s being used as” and it just so happens that for reference types the default value is a null reference. The distinction only matters when used in a context where:

  1. Nothing is taking on a value type, and…
  2. It is unintuitive from context that it is doing so.

Let’s run through a few examples that illustrate what this means.

First, while perhaps a little weird I don’t think it’s mind-blowing for most people to learn that this program prints “True”:

Module Program
Sub Main()
Dim i As Integer = 0
If i = Nothing Then
End If
End Sub
End Module

The reason is simple enough, you’re comparing an Integer (0) to its type’s default value (also 0). The problem comes in VB2005/2008 when you introduce nullable value types. Given this example:

Module Program
Sub Main()
Dim i = If(False, 1, Nothing)
End Sub
End Module

It’s understandable how someone might assume the type of i is Integer? (Nullable(Of Integer)). But it isn’t, because Nothing gets its type from its context and the only type in this context comes from the second operand and it is plain non-nullable Integer (Nothing is technically always “typeless”). A different way of looking at this problem is this example:

Module Program
Sub Main()
End Sub
Sub M(i As Integer)
End Sub
Sub M(i As Integer?)
End Sub
End Module

Again there is an understandable intuition at Nothing contributes a “nullable” hint here and that the language will choose the overload that takes the nullable but it doesn’t (it picks the non-nullable one as that is “most specific”). At a minimum, one might assume that like C#’s null the expression Nothing isn’t applicable to Integer at all and that the nullable overload will be chosen by process of elimination but that’s again based on the misunderstanding that Nothing = null (Is null?).

Trivia: In C# 7.1 a new expression was actually added to C# that does map to VBs Nothing: default. If you rewrite all three examples above in C# using default instead of null you get exactly the same behavior.

So what can be done about this? Several proposals have floated around but so far none has risen to the top:

  • Report a warning any time Nothing is converted to a value type other than a null nullable value type value (I said it, what?).
  • Pretty-list Nothing to 0, 0.0, ChrW(0), False, #1/1/0001 12:00:00 AM#, or New T (default value for any structure) anytime its run-time value will be one of these.
  • Add new syntax meaning “Null, no really!” such as Null or Nothing?
  • Add new suffix syntax (?) which wraps a value in a nullable to help type inference, e.g. If(False, 0?, Nothing)
  • Add nullable conversion operators for built-in types to make it easier to give hints to type inference, e.g. If(False, CInt?(0), Nothing)

Would love to hear your thoughts in the comments and/or on Twitter.

So let’s recap:

  • The beforetime – VB6 and VBA have Nothing, Null, Empty, and Missing each meaning different things.
  • 2002 – VB.NET only has Nothing (meaning default value in context) and C# has only null (meaning… null).
  • 2005 – C# adds default(T) (meaning default value of type T) because newly added generics create a situation where you need to initialize a value but don’t know if it’s a reference type or a value type; VB does noth… doesn’t do anything because it already has this scenario covered by Nothing.
  • 2017 – C# adds default (meaning default value in context) because there are plentiful scenarios where specifying T is redundant or impossible; VB continues to resist urge to a true Null expression (or equivalent) because:
    • The syntax would be a breaking change.
    • The syntax would not be a breaking change but would contextually mean different things.
    • The syntax would be to subtle (e.g. Nothing?); imagine having to talk about both Nothing and Nothing? out loud to explain something to a person.
    • The syntax might be too ugly (e.g. Nothing?).
    • The scenario of expression a null value is already covered by Nothing and this feature would be entirely redundant most of the time.
    • All documentation everywhere and all guidance would have to be updated to recommend using new syntax mostly deprecating Nothing in most existing scenarios.
    • Nothing and Null would still behave the same at runtime with regard to late-binding, conversions, etc.
    • This might be bringing a cannon to a knife fight.

So there you have it.

Off-topic (but related)

Here’s an example very similar to the second one above but without type inference:

Module Program
Sub Main()
Dim i As Integer? = If(False, 1, Nothing)
End Sub
End Module

This program prints 0 to the screen. It behaves exactly the same as the second example for the same reason but illustrates a separate though related problem. It’s intuitive that Dim i As Integer? = If(False, 1, Nothing) behave the same as Dim i As Integer? : If False Then i = 1 Else i = Nothing. In this case it doesn’t because the conditional expression (If) doesn’t “flow through” target type information to its operands. It turns out this breaks all expressions in VB which rely on what’s called target (contextual) typing: Nothing, AddressOf, array literals, lambda expressions, and interpolated strings with problems ranging from not compiling at all to silently producing the wrong values to loudly throwing exceptions. Here’s an example of the not compiling case:

Module Program
Sub Main()
Dim i As Integer? = If(False, 1, Nothing)
Dim operation As Func(Of Integer, Integer, Integer) =
AddressOf Add,
AddressOf Subtract)
End Sub
Function Add(left As Integer, right As Integer) As Integer
Return left + right
End Function
Function Subtract(left As Integer, right As Integer) As Integer
Return left right
End Function
End Module

This program won’t compile. Instead it reports an error on the If expression that it can’t infer the type of the expression when clearly both AddressOf expressions are intended to produce Func(Of Integer, Integer, Integer) delegates.

What’s important to keep in mind here is that solving the problems of Nothing not always meaning null (unintuitive), Nothing not hinting at nullability (unintuitive), and If(,,) not providing the context for Nothing (and other expressions) to behave intuitively (unintuitive) are all distinct problems and solving one will NOT solve the others.

47. Parentheses affect more than just parsing precedence; they re-classify variables as values

This program prints “3” to the Console:

Module Program
Sub Main()
Dim i As Integer = 3
End Sub
Sub M(ByRef variable As Integer)
variable = -variable
End Sub
End Module

The analogous program in C# would print “-3”. The reason is that in VB parenthesizing a variable makes it behave like a value–a process known as reclassification. At that point the program behaves as if you’d written M(3) rather than M(i) and no reference to the variable i is passed so it can’t be mutated. In C# parenthesizing the expression (for whatever reason) won’t change it from being a variable to a value so calling M will mutate the original variable.

Why? This has always been the behavior in VB. In fact, I just opened my copy of Quick Basic (Copyright 1985) and it’s also the behavior there. Given that pass-by-reference was the default passing convention until 2002 everything else makes perfect sense.

Trivia #1: “How the subroutine got its parenthesis” c/o Paul Vick, Visual Basic .NET Architect Emeritus

Trivia #2: When we were designing the bound tree in the Roslyn compilers, the data structure that represents the semantics of a program (what things mean) rather than the syntax (how things are written), this was a sticking point for the compiler team: whether parenthesized expressions would be represented in the bound tree. In C# parentheses are almost purely a syntactic construct used to control the precedence of how things are parsed ((a + b) * c or a + (b * c)) so much so that the original C# compiler written in C++ threw the fact that an expression had been parenthesized away along with things like white-space and comments. There were several attempts at being consistent between the languages “Can we squint and get rid of them in VB?” or “Can we just live with them in C#?” and ultimately the result per is that BoundParenthesized is a thing in the VB compiler and is not a thing in the C# compiler. In other words, the languages are different here, and we just had to accept that.

48. Me is always classified as a value—even in structures

You cannot assign to Me in VB.NET. Normally this isn’t surprising but maybe one might think that because structures are just a set of values that it would be legit to assign to Me inside of an instance method or constructor of a Structure type as a shorthand for copying but it’s still illegal and passing Me by-reference will simply pass a copy. In C# it is legal to assign to this inside a struct. and you can pass this by-reference inside struct instance methods.

49. Extension methods can be accessed by simple name

In VB, if an extension method is defined for a type and in scope in that types definition you may call that extension method unqualified inside the definition of the type:

Class C
Sub M()
End Sub
End Class
Module Program
Sub Main()
End Sub
Sub Extension(c As C)
End Sub
End Module

In C#, extension methods are only looked for with an explicit receiver (meaning something.Extension). So while the exact translation of the above wouldn’t compile in C# you can access extensions on the current instance by explicitly stating this.Extension().

Why? There’s an argument one could make that normal instance members can be accessed without explicitly qualifying them with Me. and that since extension members act like instance members everywhere else it is intuitive that they behave consistently in this context as well. VB.NET adheres to this argument. Presumably there are other arguments and other languages are free to adhere to them.

50. Static imports will not merge method groups

VB has always supported “static imports” (A Java term combining a C# modifier with the VB statement). It’s what allows me to say Imports System.Console at the top of a file and use WriteLine() unqualified throughout the rest of the file. In 2015, C# also added this capability. However, in VB if import to 2 types with Shared members that have the same name, e.g. System.Console and System.Diagnostics.Debug which both have WriteLine methods it’s always ambiguous what WriteLine refers to. C# will merge the method groups and perform overload resolution and if there’s one unambiguous result that’s what it means.

Why? I think there’s an argument to be made that VB could be smarter here like C# (especially given the next difference). But, there’s also an argument to be made that if two methods come from two different places and have no relationship with each other at all (one is not an extension method on the type defining the other) that it’s… misleading to suggest they’re all options under the same name.

Moreover, there are multiple cases in VB where this same scenario comes up where VB picks the safer route and reports ambiguity, e.g. two methods with the same name from unrelated interfaces, two methods with the same name from different modules, two methods from different levels of the inheritance hierarchy where one isn’t explicitly an overload of the other (difference #6). VB is philosophically self-consistent here. Also, VB made all of those decisions in 2002.

51 & 52. Partial-name qualification & Smart-name resolution

There are a couple of ways to think about namespaces:

  • In one way of thinking, namespaces are all siblings in a flat list and only contain types (not other namespaces). So System and System.Windows.Forms are siblings that share a common prefix by convention but System does not contain System.Windows and System.Windows does not contain System.Windows.Forms.
  • In another way of thinking, namespaces are like folders organized into a hierarchy and can contain other namespaces and types. So System contains Windows and Windows contains Forms.

The first model is particularly useful for displaying namespaces in a GUI without deep nesting. However, my intuition has always been the second. And with regard to Imports statements VB follows the second model and C# using directives behave like the first.

Consequently in VB, if I have imported the System namespace, I can access any namespace inside System without qualifying it with System. To me this is like specifying a relative path. So, if any of my examples where I qualify the ExtensionAttribute I write <Runtime.CompilerServices.Extension> instead of <System.Runtime.CompilerServices.Extension>.

In C#, this is not the case. using System does not bring System.Threading into scope under the simple name Threading.

But it gets even better, because C# does allow for this “relative path”-style partial qualification scenario specifically in the case where the code is defined in that namespace. That is to say, if you’re declaring a type inside System, within that type you may refer to the System.Threading namespace as Threading. And that’s self-consistent because you could write out a namespace declaration and a type declaration both lexically contained inside another namespace declaration and it would be weird if name lookup from the type wouldn’t find the sibling.

But it gets even worse, because though both VB and C# require that namespaces always be fully qualified within the file-level Imports statements/using directives, C# allows you to have a using directive inside a namespace declaration affecting code inside that declaration in that file and within those using directives namespaces can be specified using their simple name.

Enter Quantum Namespaces (not the official name)

But wait, there’s more! The VB model is convenient, but that convenience comes with a risk. Because what happens when System contains a ComponentModel namespace and System.Windows.Forms contains a ComponentModel namespace? It’s ambiguous what ComponentModel means. And what would happen sometimes is that you’d write all this code that just said ComponentModel.PropertyChangedEventArgs and the world would be fine (I vaguely recall that earlier versions of the designers would do this in generated code). But then you’d import System.Windows.Forms (or maybe just reference an assembly which declares a sub-namespace in one you have imported with that name and all your code would break with ambiguity errors.

So in VB2015 we added Smart Name Resolution whereby when you have System and System.Windows.Forms imported and you write ComponentModel. a
Schrödinger-style quantum superposition is created of both the realities where you’re referring to System.ComponentModel and where you’re refering to System.Windows.Forms.ComponentModel until you type another identifier, and if that identifier identifies a child namespace in both realities the wave continues, until the . after which the identifier unambiguously refers to a type that only exist in one temporal universe at which point the entire wave collapses and the cat was always dead. i.e. ComponentModel.PropertyChangedEventArgs must mean
System.ComponentModel.PropertyChangedEventArgs because System.Windows.Forms.
does not exist. This avoids many of the ambiguities that would occur from simply importing a new namespace.

But it doesn’t solve the problem of adding a reference which brings a new top-level namespace Windows into scope because top-level namespaces (absolute paths) always beat out partially qualified ones (relative paths) for various reasons (including performance). So using WinForms/WPF and UWP all in one project may still be painful.

53. Collection initializer Add methods may be extension methods

As mentioned in #33, VB generally includes extension methods when looking for things. The scenario for why you’d want this is when you want to use that concise initializer syntax for collections of complex objects, e.g.:

Class Contact
Property Name As String
Property FavoriteFood As String
End Class
Module Program
Sub Main()
Dim contacts = New List(Of Contact) From {
{"Leo", "Chocolate"},
{"Donnie", "Bananas"},
{"Raph", "The Blood of his Enemies"},
{"Mikey", "Pizza"}
End Sub
Sub Add(collection As ICollection(Of Contact), name As String, favoriteFood As String)
collection.Add(New Contact With {.Name = name, .FavoriteFood = favoriteFood})
End Sub
End Module

C# originally didn’t originally consider extension methods in this context but when we re-implemented collections initializers in the Roslyn C# compiler they did consider them. It was a bug that we decided never to fix (but not a feature that we decided to add) so this is only a difference prior to VS2015.

54. Array creation uses upper-bound rather than size

Surprised this rarely comes up, but when initializing an array in VB with the syntax Dim buffer(expression) As Byte or Dim buffer = New Byte(expression) {} the size of the array is always expression + 1.

This has always been true in Microsoft BASIC languages as far back as the DIM (it means dimension) statement has been in existence. Which, I suppose explains why it works that way, the dimension of the array is from 0 To expression. In past versions of Microsoft BASIC languages you could change the default lower-bound of arrays to be 1 (and could explicitly declare the array with arbitrary lower-bounds like 1984) in which case the upper-bound was also the length (I typically did this) but this ability was lost in 2002.

But on an even deeper level, I’ve heard tell of a language design fad way back then of making declaration syntax model usage syntax that explains why arrays are declared with their upper-bound in VB, why array bounds are specified on the variable rather than the type in BASIC and in C, the pointer syntax in C, why types are on the left in C-derived languages. Think about it, all usages of buffer(10) will use a value from 0 to 10, not 9!

55. VB array literals are f-ing magic not the same as C# implicitly typed array creation expressions

Though these two features are often used in the same scenarios they’re not the same. The main difference being that VB array literals are naturally typeless (like lambdas) and get their type from their context, and in the absence of a type context from their element expressions. The spec illustrates this well:

  • CType({1, 2, 3}, Short()) does not mean CType(New Integer() {1, 2, 3}, Short()) because it is impossible to convert an Integer array into a Short array.
  • CType({1, 2, 3}, Short()) reclassifies the array literal to mean New Short() {1, 2, 3}. There is no spoon.

This is actually pretty cool because it means things can happen with a VB array literal that can’t with a C# implicitly typed array creation. For example, passing an empty one:

  • Dim empty As Integer() = {}

Creating an array of typeless expressions:

  • Dim array As Predicate(Of Char)() = {AddressOf Char.IsUpper, AddressOf Char.IsLower, AddressOf Char.IsWhitespace}

Performing per element conversions (intrinsic or user-defined):

  • Dim byteOrderMark As Byte() = {&HEF, &HBB, &HBF} ' No byte literals neeed.

And because target-type isn’t only inferred from array types but also IList(Of T), IReadOnlyList(Of T), ICollection(Of T), IReadOnlyCollection(Of T), or IEnumerable(Of T) you can very concisely pass a variable number of arguments to a method that takes one of those types, rendering ParamArray IEnumerable unnecessary.

Why? Prior to writing this doc I thought this difference was mostly just VB going the extra-mile but I now believe it’s something much simpler. Prior to the introduction of local type inference in 2008 both VB and C# allowed you to initialize an array declaration with the {} “set notation” syntax but you couldn’t use that syntax anywhere else in the language (except attributes, I think). What we think of now as array literals is really just a generalization of what that syntax could do to any expression context + a few other niceties like inferring from the above generic interfaces. Which is really elegant.

56. Anonymous type fields can be mutable AND are mutable by default

This doesn’t impact the anonymous type fields created implicitly by LINQ but the ones you explicitly create may be mutable or not, up to you.

Details on why and how here.

57. Neither CType nor DirectCast are exactly C# casting

There is no exact match between casting/conversion operators between VB and C#.

VB CType

  • Supports user-defined conversions
  • Supports reference conversions (base class to derived)
  • Supports intrinsic conversions, e.g. Long to Integer (see Conversions section)
  • Unboxes complex value types directly
  • Does NOT unbox primitive types directly
  • Does NOT support dynamic conversions (use CTypeDynamic function)

VB DirectCast

  • Does NOT support user-defined conversions
  • Supports reference conversions
  • Does NOT support intrinsic conversions (cannot convert Integer to Byte)
  • Unboxes complex value types directly
  • Unboxes primitive types directly (hence the name)
  • Does NOT support dynamic conversions

C# casting – (Type)expression

  • Supports user-defined conversions
  • Supports reference conversions
  • Supports intrinsic conversions
  • Unboxes complex value types directly
  • Unboxes primitive types directly
  • Supports dynamic conversions

Between the two of them CType is the closest to C# casting in that it can be used in the broadest set of scenarios. In fact, from the perspective of the language it is the conversion operator. But VB and C# allow and prohibit different conversions, have different semantics for the same conversions, and in some cases generate different IL for those conversions. So there’s no way to get exactly the C# set of conversions no more, no less, with exactly the same semantics, and exactly the same code generated with a single operator all the time, nor should there ever be.

In reality, everyone can use CType except for dynamic conversions (conversions which look for a user-defined conversion operator at runtime). CType supports every scenario DirectCast supports and more and in every case where they can both be used they will generate the same IL, with one exception: when converting from Object (or ValueType) to a primitive type, instead of emitting a CLR “unbox” instruction the compiler emits a call to a VB runtime function which will succeed if the type of the object is the target type OR if the value of the object can be converted to that type, e.g. widening a Short to Integer; meaning it will succeed more often than it would in C#. But this only supports intrinsic conversions between primitive types. In an extremely narrow set of scenarios this could matter but for the vast majority of cases it won’t.

Why? The languages support different conversions. The conversion operators should support the conversions the languages support not the conversions other languages support for no particular reason.

58. The precedence of certain “equivalent” operators is not exactly the same

See the specifications for the full tables of operator precedence but they are not the same between the languages, so 5 Mod 2 * 3 evaluates to 5 in VB, the “equivalent” expression in C#, 5 % 2 * 3 evaluates to 3.

Operator precedence is probably the oldest part of either language family. I only noticed this when I considered the impact of operators which only exist in one language (e.g. integer division (\) in VB) on operators after it which might otherwise be at the same level, but the differences appear to be far more pervasive. You have been warned!

59. String concatenation is not the same + and & are not the same with regard to String concatenation and + in VB <> + in C#

Let’s just talk about how VB + (addition) and & (concatenation) differ from each other and from C# +.

Between String and primitive types:


  • “1” + 1 = 2.0
  • “1” & 1 = “11”


  • "1" + 1 == "11"

Between string and types that don’t overload + or &


  • “obj: “ + AppDomain.CurrentDomain ‘ Error: + not defined for String and AppDomain.
  • ”obj: “ & AppDomain.CurrentDomain ‘ Error: & not defined for String and AppDomain.
  • ”obj: “ + CObj(AppDomain.CurrentDomain) ‘ Exception, no + operator found.
  • ”obj: “ & CObj(AppDomain.CurrentDomain) ‘ Exception, no & operator found.


  • "obj: " + AppDomain.CurrentDomain == "obj: " + AppDomain.CurrentDomain.ToString()
  • "obj: " + (object)AppDomain.CurrentDomain == "obj: " + AppDomain.CurrentDomain.ToString()
  • "obj: " + (dynamic)AppDomain.CurrentDomain == "obj: " + AppDomain.CurrentDomain.ToString()

Between numeric types


  • 1 + 1 = 2
  • 1 & 1 = “11”


  • 1 + 1 == 2

Between String and Enum types


  • “Today: ” + DayOfWeek.Monday ‘ Exception: String "Today: " cannot be converted to Double.
  • “Today: ” & DayOfWeek.Monday = “Today: 1”
  • “Today: ” & DayOfWeek.Monday.ToString() = “Today: Monday”


  • "Today: " + DayOfWeek.Monday == "Today: Monday"

Pet peeve: I really dislike that + is even allowed for string concatenation in VB. It’s there for legacy purposes, + always concatenated strings but its current behavior is more like a bug than anything. Why? Because:

  • “10” - "1” = 9.0,
  • “5” * “5” = 25.0,
  • “1” << “3” = 8, and
  • “1” + 1 = 2.0, but
  • “1” + “1” = “11”

Every other arithmetic operator converts the strings to numbers. + being inconsistent is a design bug.

In summary, don’t use + because it looks like the way it’s done in other languages. To get the behavior you intend use & because this dedicated operator exists to unambiguously specify that intent (concatenation, rather than addition). Also, watch out when concatenating enum values, they behave just like their numeric values in that context.

60. Division works sanely: 3 / 2 = 1.5

From time to time I conduct an experiment, I walk up to a random person and ask them “What is three divided by two?”. Most people say 1.5 or one and a half. Only the most indoctrinated among us squint at me and say “That depends. What are the types of the three and the two?”

All of the difference between VB and C# is summed up in this.

Ok, maybe not all of it. That would be pretty mean of me to tell you 60 essays in. Also, I really want you to read the rest. If you really want the C-style behavior, which I suppose is the answer to the question “How many times does 5 go into 9 whole?”, use the integer division operator \. I suppose another justification is that division is closed under the set of integers, excepting division by 0 (which would be relevant if an INumeric interface ever showed up).

61. ^ isn’t exactly Math.Pow

That is to say, it’s not just an alias for Math.Pow. It’s an overload-able operator that has to be explicitly be opted into outside of primitive types. It saddens me how often a custom numeric type doesn’t support it (looking at you System.Numerics.BigInteger).

Trivia: F# also has an overload-able exponentiation operator, **, but when overloading the operator VB and F# emit different names, op_Exponent and op_Exponentiation respectively. Though F# actually looks for a method called Pow on the operand types. Meaning these languages don’t inter-operate well with each other. A sad fact I’d like to see fixed one day.

62. Operator =/<> are never reference equality/inequality

Sometimes in C# == will use (overloaded) operator equality, sometimes language equality, and sometimes reference equality (if the operand types don’t overload equality, are object, or are interface types). In VB this operator will never mean reference equality; VB has separate operators (Is/IsNot) for reference equality.

Story time: At some point in the history of Roslyn we had a class hierarchy that overloaded value equality. Actually, we had two such hierarchies. One day we decided to abstract over both with an interface hierarchy. All of the VB code correctly broke when updated to use the interfaces because = stopped being valid, but there was a bug tail on the C# side because much code that was previously using overloaded value equality silently started using the much stricter requirement of reference equality.

63. Operator =/<> on strings are not the same (or any relational operators for that matter)

String equality in VB is different in a few ways.

First, whether string comparisons use a binary comparison (and thus are case-sensitive) or a culture-aware (and thus are case-Insensitive) is governed by whether Option Compare Binary or Option Compare Text is specified at the file level and/or what its setting is at the project. The default for all projects in VS is Option Compare Binary, btw.

This setting governs all explicit and implicit String comparisons (but not Char comparisons) that occur in the language but not most API calls. So:

  • Equality/Inequality: “A” = “a” / “A” <> “a”
  • Relation: “A” > “a”
  • Select Case statements: Select Case “A” : Case “a”

But not:

  • Calls to Equals: “A”.Equals(“a”)
  • Calls to Contains: ”A”.Contains(“a”)
  • The Distinct query operator: From s In {“A”, “a”} Distinct

But there’s a second far more impactful difference that may surprise you: in the VB language, null strings and empty strings are considered equal. So, regardless of the setting of Option Compare, this program will print “Empty”.

Module Program
Sub Main()
Dim s As String = Nothing
If s = "" Then
End If
End Sub
End Module

So technically s = “” is VB shorthand for String.IsNullOrEmpty(s)

Practically speaking the distinction doesn’t trip people up as often as you might think since the operations one can do on a null string and an empty one are almost exactly the same. You never invoke a member of an empty string because you know what all the answers will be and concatenation treats null strings as empty.

Why? I think of Option Compare Text as more a back-compat option but I get why it existed in the first place. There are a lot of contexts where you want string comparisons to be case-insensitive.

In fact, most contexts where I use strings I want them case-insensitive.

Essentially all contexts outside of passwords and encryption keys. But everywhere else I don’t want my typing a literal lazily to affect my results. Yes, I’m that monster who uses case-insensitive collation on SQL Server because I value my productivity. And if you consider that VB’s history includes not only VB6 but VBA for Office products like Excel and Access and VBScript for Windows and that one web browser that one time, … ain’t nobody got time for case-sensitivity. That said, I accept that .NET is a generally case-sensitive API and I don’t use Option Compare Text because it only affects the language. If there was an setting that affected all the .NET APIs too I’d flip that sucker on and never look back.

As for null being treated as empty, I have a theory. VB6 didn’t have null strings. The default value for String was "". So VB and its runtime philosophically treat the default value for String to be an empty string. In fact, that’s one of the main advantages of using the VB Strings functions like Left and Mid over the methods on String. The runtime functions also treat null like empty. So Len(CStr(Nothing)) = 0 and Left(CStr(Nothing), 5) = “” while CStr(Nothing).Length or CStr(Nothing).Trim() would just blow up.

Fortunately, now you can sort of get this same productivity with the ?. operator (at least the not throwing part).

Why it matters:

So the top-of-mind issue for me with this is that this difference happens everywhere a two string values are compared in the language, so any string comparison in any expression. Including query expressions! The way VB string comparison is implemented is that every time you type “String A” = “String B” that turns into a call to Microsoft.VisualBasic.CompilerServices.Operators.CompareString and when a string comparison in a query expression or lambda expression is converted to an expression tree it shows up in the tree not as an equality comparison but as a call to that function. And invariably every new LINQ query provider throws an exception when it encounters this node. They just don’t expect the pattern because their libraries weren’t tested with VB (or well enough). Which usually means support of that library is delayed until someone can explain to them how to recognize the pattern. This happened with LINQ-to-SQL, LINQ-to-Entities, and a few other ones I encountered during my time at Microsoft. Everything seems fine until a VB developer compares two strings them BOOM!

So, aside from the semantics of string comparison being slightly different than C# it causes a real problem for VB customers using LINQ on new providers. The options to fix it are to 1) change way VB generates expression trees to outright lie or 2) change the way VB generates equal to use a pattern more easily recognizable by LINQ providers. I’m in favor of the latter, though it does require servicing the VB runtime (probably).   

Trivia: Note that I said most API calls. Because Option Compare does actually affect calls to VB runtime string functions such as InStr, Replace, and other members of the Microsoft.VisualBasic.Strings module. How does a compilation setting affect the operation of an already compiled library function, you ask?

Well, you know the way the compiler can pass the current filename or line number in as the value for some optional parameters if they’re properly decorated? Turns out before that feature was added the same scheme was used for the Strings functions: The compiler passes in a value indicating the setting at that point in the program to an optional parameter.

64. Nullable value types use three-valued logic (null propagation in relational operators)

VB and C# handle nullable differently. Specifically, in the area of null-propagation.

If you work a lot in SQL you’re likely very familiar with null-propagation. In short, it’s the idea that given some operator (e.g. +), that if one or more of its operands is null the result of the entire operation is also null. This is similar to the ?. operator where given the expression obj?.Property if obj is null the entire expression results in a null value, rather than throwing an exception.

When dealing with various unary and binary operators and nullable value types, VB and C# both propagate nulls. But, they differ in when they do in a key area: relational operators.

In VB, specifically when dealing with nullable value types, if either operand is null the entire expression is null, with two exceptions. So, 1 + null is null and null + null is null. But this doesn’t just apply to arithmetic operations, it also applies to the relational operators (e.g. = and <>) and this is where C# differ:

  • All of the VB relational operators other than Is/IsNot return Boolean?
  • All of the C# relational operators (==, !=, >, <, >=, <=) return bool instead of bool?

In VB (again specific to nullable value type arguments), comparing a null value with any other value results in null. That is to say, instead of = returning its usual Boolean result, it returns a nullable Boolean? which may be True, False, or null. This is known as three-valued logic. In C# the result of the comparison is always a non-nullable bool value which is fittingly known as two-valued logic.

Note that I said any value. That includes null itself. So NULL = NULL is NULL, not TRUE, in VB.

So a couple of fun consequences to the respective designs:


That broke my mind. Null is not greater than itself, but is equal to itself, and yet not greater than or equal to itself in C#.

And that’s the crux of the issue. If C# used the VB model, the most natural way to ask the question “Is this value null?” in C# (if (value == null)) would fail every time. Here’s a 2004 post saying as much. VB doesn’t have this problem because VB has separate operators for value equality (=/<>) and reference equality (Is/IsNot) so the idiomatic way to check for null in VB is Is Nothing returns a regular non-nullable Boolean.

Earlier, I mentioned an exception to the rule that if either operand is null the entire expression is null in VB. That exception is with the And/AndAlso and Or/OrElse operators.

When the operands are Integer? type (and other integrals), both VB and C# propagate nulls as you would expect:

  • 1 AND NULL is NULL
  • 1 OR NULL is NULL

When the operands are Boolean? type, in VB it’s more complicated.


In other words, if the True/False result can be definitively computed based on knowing one operand the result will be that value, even if the other operand is null. This also means short-circuiting logic operators AndAlso and OrElse work as expected.

In C# it’s not legal to apply either the short-circuiting (&&/||) or non-short-circuiting (&/|) logic operators to nullable boolean (bool?) operands. Which, isn’t as problematic as I first thought because given that all the relational operators produce non-nullable booleans it’s fairly uncommon for a nullable boolean operand to sneak into an expression anyway.

Why does it matter?

Usually the VB behavior is only surprising when someone writes code like this:

Imports System.ComponentModel
Class BindableRange
Implements INotifyPropertyChanged
Private _EndDate As Date?
Property EndDate As Date?
Return _EndDate
End Get
Set(value As Date?)
' This line here:
If value = _EndDate Then Return
_EndDate = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(EndDate)))
End Set
End Property
Public Event PropertyChanged As PropertyChangedEventHandler _
Implements INotifyPropertyChanged.PropertyChanged
End Class
Module Program
WithEvents Range As New BindableRange
Sub Main()
Range.EndDate = Today
Range.EndDate = Today
Range.EndDate = Nothing
Range.EndDate = Nothing
End Sub
Private Sub BindableRange_PropertyChanged(sender As Object, e As PropertyChangedEventArgs) _
Handles Range.PropertyChanged
Console.WriteLine(e.PropertyName & " changed.")
End Sub
End Module

You might be surprised to learn that this program prints “EndDate changed” thrice three times instead of two. Remember when I said that in VB null does not equal itself? Because it never equals itself when the EndDate property set checks to see if the new value is the same as the old value the check fails the second time the code assigns Nothing to the property.

This is usually when the VB developer says, “Ok, I see how this works. I’ll invert it”:

If value <> _EndDate Then
    _EndDate = value
    RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(EndDate)))
End If

But that doesn’t work either! In fact the event will never raise in the code now. Rather, it will only raise if the value changes between two non-nullable values but not been a value and a null or vice versa. Because null is neither equal nor unequal to itself. The way to correctly fix the bug is to write this:

If value Is Nothing AndAlso _EndDate Is Nothing Then Return
If value <> _EndDate Then Return

Why is it different/Which is better?

I mentioned why the C# team decided to go with their design and that those concerns do not generally apply to VB at all. When thinking about null values (either with nullable value types or reference types) I often see a conflict between two notions of what null represents.

One way to look at null is that it’s a value that means “none” or “does not exist”. Usually this is what it means with reference types.

But in other cases null means “unspecified” or “unknown”. This is often what is meant by optional parameters which weren’t provided; not that the caller intended you use no comparer but that they were fine with you using the default comparer. And if you look at the Roslyn code base there is actually a type called `Optional(Of T) which is used to describe this notion for reference types, because it’s otherwise impossible to distinguish between values that are intended to be null and values which simply haven’t been provided.

And if you use the latter interpretation, NULL as “an unknown value”, all of the three-valued logic in VB makes sense:

  • If I ask you “Is three greater than an unknown value?” you can only answer “I don’t know”.
  • Likewise “I have two boxes containing unknown items, are they the same item?” “I don’t know”.

And that’s probably why this is also the interpretation that SQL databases use by default. By default, if you try to compare NULL in SQL with any value the answer you get is NULL. And that makes sense dealing with data especially. Everyone reading this post right now would have a NULL in the DateOfDeath column. That’s not the same as us all dying on the same day. If several people fill out a form, most people will not fill out their middle names though they may (it’s optional). This does not mean that all of those people have the same middle name though some people legitimately have no middle name and you could argue that the value of the middle name is the empty string but you get how the meaning of NULL is open to interpretation particularly in SQL databases (with exceptions).

Which brings us back to VB. What’s the killer scenario for nullable value-types in 2008 when full generalized support for nullable value types were added to VB?


The VB model provides consistency between the database where these nullable value types are likely coming from and the language and between comparisons as they appear in a LINQ query and as they run on the server. That’s super compelling to me!

But there’s a catch. In SQL Server at least, there’s an option SET ANSI_NULLS OFF which causes SQL expressions to behave more like C# so you can write WHERE Column = NULL. And I’ll admit, I usually set this to OFF in the past (along with making my database collation case-insensitive). So, I reached out to the SQL Server team (years ago) for guidance. I asked, “What’s the deal with this option? I use it. Is it the way to go and should we add something like Option ANSI_NULLS Off to VB.NET?”. Their response is basically summed up on the docs for the option:

In short, that option is a back-compat thing, will quite possibly go away in the future, and they’d like all humans using SQL Server to adapt to the current VB way of thinking.

So there you have it!

65. Overloaded operators don’t always have a 1:1 mapping

There are cases where VB supports two notions of an operator that other languages would unify, e.g. regular vs integral division. In those cases, overloading the operator in VB may silently overload other operators to be usable from other languages.

Likewise, there are cases where other languages overload certain operators separately, e.g. logical and bitwise negation or signed and unsigned bitshifting. In those cases, VB may recognize such overloads defined in other languages if they are the only flavor available, and in cases where both flavors are available VB may ignore one flavor entirely.

Section 9.8.4 of the spec is the definitive list on these mappings.

66. Function() a = b is not the same as () => a = b

I’ve seen this a few times in translated code. It’s easy to get into the habit of thinking C#’s () => expression syntax always maps to a Function() expression syntax in VB. However, the Function() lambda is only for expressions–lambdas which return something–which assignment is not in VB. Using this syntax with a body of the form a = b will always produce a delegate which compares a and b (returning a Boolean) rather than assigning b to a. However, because of VB delegate relaxation this lambda can still safely (and silently) be passed to a Sub delegate (on that does not return a value). In these cases the code just silently does nothing. The correct translation of () => a = b from C# to VB is Sub() a = b. This code is a statement lambda which correctly contains an assignment statement and can be used for its side-effects.

It has always been the case that whether the = operator denotes a comparison or an assignment is determined by the context; in a statement context (such as a Sub lambda) it denotes assignment, in expression contexts(such as a Function lambda) it denotes comparison.

67. An Async Function lambda will never be interpreted as an async void lambda

In C# it is syntactically ambiguous when writing an async lambda expression whose body does not return a value whether that lambda is intended to be a Task-returning async lambda, or a void-returning async lambda. There’s a rule in C# overload resolution to prefer the Task-returning interpretation if available.

VB.NET does not have this syntactic ambiguity as void-returning Async lambdas use the Async Sub syntax and Task-returning and Task(Of T)-returning lambdas use the Async Function syntax. That said, there’s a different situation that can occur in VB which is relaxing a Task-returning Async lambda into a void-returning delegate type by dropping its return value. This lambda does not behave like an Async Sub and a warning was added in the event that this relaxation ever occurs.

68. Queries are real(er) in VB

To illustrate my sensational title, look at this example in VB:

Class Foo
'Function [Select](Of T)(selector As Func(Of String, T)) As Foo
' Return Me
'End Function
Function Where(predicate As Func(Of String, Boolean)) As Integer
Return 0
End Function
End Class
Module Program
Sub Main()
Dim c As New Foo
Dim q = From x In c Where True Select x
End Sub
End Module

It will not compile in VB for two reasons but will in C#. First, the type Foo doesn’t have a Select method and so is not queryable, so it’s not even legal to use a Where operator on it. But were you to un-comment the definition of Select to resolve that error the final Select operator won’t compile now because Integer isn’t queryable. In C# however the translation is specified syntactically such that the entire query reduces to a simple call to .Where (the final select is elided). Because it doesn’t manifest all of the query clauses written it doesn’t give errors when the pattern is malformed.

This is a difference that only comes up in language design, or when trying to represent LINQ in an API. But the way that queries are designed in VB and C# are different. Specifically, C# queries are modeled on the idea that they are but a “syntactic transformation”, which means the language spec defines them in terms of translating the query syntax into a different syntax, and all the semantic analysis happens AFTER the final translations are done. In a way this means the language is “hands off” about what things might mean in the middle and doesn’t presume to make guarantees about anything.

In VB on the other hand the language meticulously describes query operators with somewhat strict semantics and may require that objects adhere to certain constraints at intermediate steps which C# might only enforce after the final transformation or not at all if the translation doesn’t require them.

Example questions this forced us to ask during Roslyn include “Do range variables exist?” and “Do range variables have types?”. The answer isn’t clearly the same if you look at one language or the other. e.g. in VB you can explicitly type the variables declared by a Let query operator; in C# you can’t. But, let me give you example of a program whose VB equivalent will not compile in any version of VB but does compile in C# 2012 despite being CRAZY:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CSharpExamples
struct Point
public int X { get { return 0; } }
public int Y { get { return 0; } }
class Foo
public IEnumerable<Point> Select<T>(Func<string, T> selector)
return new Point[0];
static class Program
static void Main(string[] args)
var c = new Foo();
var q = from X in c let Y = "" select Y.CompareTo(1);

Why is this insane, you ask? Well, when X is introduced its type is string. Then the let clause introduces a new range variable Y which is also typed as string. But the underlying query operator doesn’t produce a sequence of anonymous types, it actually produces a sequence of type Point which just happens to have properties X and Y with the same names as our range variables but both of type int and utterly unrelated to the X and Y “declared” in the query so when you refer to Y in the select clause it has type int and the members of int and just… compiles.

That’s what I mean when I say “Do range variables exist and do they have types?”. Prior to VS2015 in C# an argument could be made that the answer is “no”. That said, in Roslyn we actually tightened up the rules in C# here a little bit and this program will no longer compile. Thinking up these two examples has hurt my head enough, whatever other monstrous examples may exist (and I’m sure they do) will have to spring forth from the mind of another.

Why? It’s a tradeoff between the ease and elegance of describing a feature in the specification and to users and implementing it as a simple syntactic transformation and the particularity of the experience you want to create and all the work that entails. I can’t say there’s a right or wrong approach for every situation and the VB and C# design teams and the languages themselves just have different precedents and principles here.

69 & 70. As clause in query From doesn’t not always call cast; bonus from ‘As’ clause can perform implicit user-defined conversions

(Because after the last one this is the shocker, but…)

When you write From x As Integer In y in VB it’s not exactly the same as from int x in y in C#.

First, in C# putting a type there always means you’re casting (or converting) the source collection. There will be a call to .Cast<T>(). In VB it may just be a stylistic choice to eschew type inference and so if the specified type is the element type of the collection no casting is performed.

Secondly, you aren’t limited to conversions which can be statically performed by the .Cast method (known as reference conversions). You can put any type there that the source type can be converted to – including via user-defined conversions and the query will translate to a call to .Cast or .Select appropriately.

Why? No idea. But the VB behavior is very self-consistent. For example, when you type For Each x As T In collection, that As T part may cause any legal conversion to take place or none at all. So the behavior of From range variables and As clauses is consistent with For Each loops (and really, all As clauses).

71-75. Select clause isn’t required at all, can appear in the middle of the query, can appear multiple times, and can declare multiple range variables with implicit names, or explicit names


  • From x In y Where x > 10 is fine. Let’s just say the Select is implicit.
  • From x In y Select x Where x > 10 is perfectly legal.
  • From x In y Select x is really the same as From x In y Select x = x where the x on the left is a new range variable named x and the x on the right is the range variable in scope before the Select. After the Select the old x is out of scope.
  • From x In y Select z = x.ToString(), now x is gone entirely.
  • From x In y Select x.FirstName is really the same as saying From x In y Select FirstName = x.FirstName
  • From x In y Select x.FirstName, x.LastName is sorta like saying From x In y Select New With {x.FirstName, y.LastName} except now no range variables are in scope. But from the perspective of the result of the entire query expression they produce the same IEnumerable(Of $AnonymousType$) so it is almost never needed to explicitly create an anonymous type.

Why? Ask Amanda Silver. But, I can guess!

  • I’m guessing the Select can go anywhere because it’s already jarring from a SQL perspective that the Select clause doesn’t go first, so this way you can put it second. The original design proposal for LINQ in VB did attempt to let you put the Select clause first just like in SQL but from a tooling perspective putting From first was THE best way to go.
  • I’m guessing you can omit it because there’s no reason to require it.
  • I’m guessing you can Select multiple expressions because you can do that in SQL, and it doesn’t make sense to do it any other way, and avoids needing to use anonymous type syntax explicitly. Also there’s a huge precedent in VB for allowing comma separated lists of things.
  • I’m guessing Select implicitly declares names because if it didn’t you’d have to repeat the names and you need names because if there were no names you’d have nothing to refer to in subsequent clauses and projecting subsets of columns from tables in databases is an extremely common scenario.

Does it matter? These differences are top of mind for me because of an error people occasionally get and can’t understand in situations like this:

Module Program
Sub Main()
Dim numbers = {1, 2, 3}
' BC36606: Range variable name cannot match the name of a member of the 'Object' class.
Dim q = From n In numbers
Select n.ToString()
End Sub
End Module

BC36606: Range variable name cannot match the name of a member of the 'Object' class. and BC30978: Range variable '…' hides a variable in an enclosing block or a range variable previously defined in the query expression both can result from unintentionally introducing a range variable with the same name as a member of `Object` or a local variable in scope outside of the query, always in the scenario where the query is selecting a single value which is intended to be anonymous. The workaround is to parenthesize the expression (n.ToString()) because this suppresses the implicit naming. I’d like one day to just have the language not report this error in this common case.

76+. Method invocation and overload resolution are different

I tried… to fit it all… into a one-pager. I wasn’t… strong… enough. Will… post remaining 20-25 differences next week before summer.