Converting a large VB.NET project to C#

This wasn’t the sexiest project we’ve ever worked on, but a combination of good tooling, careful preparation, and a fair amount of mind-melting compiler error resolution made this one of our best remodeling efforts of 2018.

Why would you do this?

Seriously, wouldn’t you rather poke toothpicks in your eyeballs than convert a bunch of VB.Net code to C#? Maybe, but for us, there were several good reasons to tackle this:

  1. With the right tools and know-how, it’s not terribly difficult or time-consuming. This project was fair sized (3,000 files, 22K LOC), and we were able to complete the conversion in about 60 hours of time over a couple of months calendar time.

  2. Our team (and the workforce as a whole) has far greater experience and expertise in C# than VB.Net. I am not a VB hater, but the future does not belong to VB.

  3. Although Microsoft makes an effort to provide code samples in both C# and VB.Net, most other .NET resources publish code, samples, and the like in C#.

To sum up, we thought that we’d be equipped to write better code faster and more reliably in C# than in VB, both now and in the future.

Before Getting Started

  • Types - Read up on static vs. dynamic types, implicit and explicit type conversions, anonymous types and the differences between VB and C#.

  • Tests - I would highly recommend as comprehensive a set of automated UI tests as possible. If you don’t have good coverage, this is a good excuse to make it happen. There are numerous tooling options - we use Cypress and Selenium. The more coverage you have, the easier it will be to find and resolve any issues after conversion.

  • Scheduling - Make sure to afford ample time for this conversion in your project schedule. Ideally, you’ll want to freeze feature development after you finish the automated conversion and begin fixing compiler errors. If absolutely necessary, you can begin new features by branching off of your conversion branch once it’s relatively stable.

  • People - If you’re more of a big picture kind of person, I’d highly recommend you find someone who is detail-oriented to assist with grinding through compiler errors, search and replaces, and (bonus points!) regular expressions.

Tools

  • VB Conversion Tool, from $50 per month or $495 for a perpetual license. Here’s a writeup on a few other options as well.

  • ReSharper for help with refactorings.

  • Regular Expressions (I know, now you have two problems).

  • For large projects, I’d recommend spinning up a VM and running the conversions there, then pulling the branch down and fixing errors locally.

The Process

Pre-conversion

Although VB.Net and C# are very close cousins (syntax notwithstanding), there are a few key differences which will cause problems during conversion. These can be dealt with either before conversion or after. (Hello, compiler errors.)

  • The main difference is that VB allows for dynamically typed variables (not to be confused with implicit types that C# supports). Because of this, it’s recommended that you turn Option Strict On in the VB project. Like so.

    Option Strict Option

    • Turn this option on, build your project, and go fix any errors it raises. You can also let these ride, but you’ll be fixing them anyway, so you might as well deal with them in one fell swoop instead of mixed in with other compiler errors. This also enables you to complete and release part of the conversion work before fully freezing development or even fully committing to the C# conversion. (Even if you stick with VB, you’ll have cleaner and more maintainable code.)

    • The conversion tool will help call out any other issues that should be resolved before conversion.

    • Run your tests, and make sure everything passes. Fix any tests that don’t, as otherwise you’ll be unsure whether the failed tests were caused by the conversion.

Automated Conversion

  1. Create a separate branch and check it out into a separate working copy. Lots of files are going to be deleted and created, and there’s no sense in churning through all that multiple times if you need to switch back to another branch.

  2. Run the code through the converter. I won’t go into great detail here as the tool does a fairly good job of walking through issues (potential and actual) in its wizard. By default, it puts your project into a (configurable) CS folder in your project, so you can run this repeatedly and discard what does not pass muster.

    • The converter features many optional configurations (e.g. search/replace, reduce dependencies on VB-specific functions, formatting).
  3. Immediately after the automated conversion, commit your code. It probably won’t compile, but it may take you several days to get back to code that does compile, so in the interest of not losing progress, commit. Also, commit after each batch of compiler error fixes so that you have a history of the various types of errors you encountered.

Cleanup by hand

  • Build. If you’re lucky, you’ll get no compiler errors. However, if your codebase is large or has had many developers over the years, chances are you’re going to have a crop of errors.

  • Divide and conquer. Instead of going through your error list one by one, start by finding duplicates of the same error. Find a way to solve it with a regular-expression-based Search and Replace, and you’ll feel like the Brave Little Tailor.

A few common errors we found

  • Removing VB library dependencies - For example, things like this <%#Microsoft.VisualBasic.Strings.Format(DataBinder.Eval(Container.DataItem, "TransactionDate").ToString(), "d")%>

    can be converted to:

    <%# string.Format("{0:d}", DataBinder.Eval(Container.DataItem, "CorrespondenceDate"))%>

  • Automatic Type Conversions that work in VB:

    If (detailRow("rowId") = rowId) Then

    don’t in C#

    if (detailRow["rowId"] == rowId){

    You’ll need to add an explicit cast or conversion. That will also be the case for snippets like this:

    Dim cartRow As ds.CartRow = CartDataTable.Rows(0).

    CartDataTable.Rows[0] in C# returns a DataRow and doesn’t like being assigned to an object of type CartRow (a narrowing conversion).

  • Nullables - int x = y; (where y is int?) throws an error. If applicable, use y.GetValueOrDefault

  • Private Event Handlers - If event handlers bound from ASPX controls are private, you’ll probably need to convert them to protected in C#. Using regular expressions, replace

    private void ([^\(]*)\(object sender, SomeEventArgsClass e\)

    with

    protected void $1(object sender, SomeEventArgsClass e)

  • Objects vs. Strings - if(ViewState["ViewStateID"] != Session["SessionID"]): In VB, both operands are cast to strings and the comparison is done by value. In C#, though, they are objects and thus are evaluated by reference, meaning the condition will never evaluate to true. Fix with if(!string.Equals(ViewState["ViewStateID"],Session["SessionID"])).

  • Missing Click Handlers - In VB .aspx pages, many controls were automatically wired up to click handlers in the code-behinds but had no OnClick=".." attribute. These were a bit tricky to resolve, but using regular expressions, we found all asp:Button’s that lacked an OnClick and added the appropriate handler.

  • Case Senstivity - VB is case insensitive. C# isn’t. We didn’t have any issues with variables being addressed inconsistently, but this could get tricky if you’ve been sloppy!

  • Duck Typing - If SomeString - the PHP programmer in me loves this - make the variable a condition and see if it’s true or false. C#, however, doesn’t. After conversion, the code looks like this: if(SomeString), which doesn’t compile. We fixed this with a string extension method ToBooleanVB, which implements VB’s somewhat quirky truth logic for strings.

Whew! It compiles

Congratulations! But you’re not done yet.

  • If you had any VB Razor templates (.vbhtml), those aren’t converted automatically by the tool. In our project, we only had a handful of these (most pages were .aspx), so we left them as-is and are converting them by hand as we touch those files. .vbhtml templates will still work in a C# project.

  • Once you get it compiling, run your unit and browser tests and address any issues. You might consider waiting to convert your unit test projects to C# so that you can rule out conversions issues in your tests.

  • Perform user acceptance testing on your site or app to identify any gaps.

  • Finally, deploy that sucker, have a beer with your team, and go far away from a computer for a while.

Want to be alerted when we publish future blogs? Sign up for our newsletter!