Ruby.CodeCompared.To/Visual Basic .NET

An interactive executable cheatsheet for Rubyists learning Visual Basic .NET

Ruby 4.0 VB.NET (.NET 9)
Program Structure
Hello World
puts "Hello, World!"
Option Strict On Imports System Module HelloWorld Sub Main() Console.WriteLine("Hello, World!") End Sub End Module
Every VB.NET executable begins with a Module containing a Sub Main() entry point. Console.WriteLine() is the equivalent of Ruby's puts. Option Strict On is the recommended mode β€” it enforces explicit type declarations and prevents implicit narrowing conversions.
Comments
# Single-line comment puts "code" # inline comment =begin Multi-line comment block =end
Option Strict On Imports System Module CommentsDemo Sub Main() ' Single-line comment β€” the apostrophe is the only comment syntax Console.WriteLine("code") ' Inline comment ' VB.NET has no block comment syntax. ' Multi-line comments require an apostrophe on each line. ' Rem is an old-style alternative (rarely used): Rem This is also a comment End Sub End Module
VB.NET uses an apostrophe (') for comments β€” there is no block comment syntax. The old BASIC keyword Rem (short for "remark") also works but is rarely seen in modern VB.NET code. Unlike Ruby's =begin...=end, each line of a multi-line comment needs its own apostrophe.
Option Strict β€” Type Safety Mode
# Ruby is dynamically typed β€” no equivalent declaration needed greeting = "hello" number = 42 puts greeting.length
Option Strict On ' Recommended: disables implicit narrowing conversions Option Explicit On ' Required by default: all variables must be declared with Dim Imports System Module OptionDemo Sub Main() ' With Option Strict On, every variable needs an explicit type Dim greeting As String = "hello" Dim number As Integer = 42 Console.WriteLine(greeting.Length) ' 5 Console.WriteLine(number.ToString()) ' "42" End Sub End Module
Option Strict On is the strongly-typed mode: implicit narrowing conversions (like assigning a Double to an Integer) are compile errors. Option Explicit On (the default) requires every variable to be declared with Dim. Legacy VB code sometimes uses Option Strict Off and Option Explicit Off, which allow dynamically-typed Object variables β€” avoid this style in new code.
Imports (require)
require 'date' puts Date.today
Option Strict On Imports System Imports System.Collections.Generic Imports System.Linq Module ImportsDemo Sub Main() Dim today As DateTime = DateTime.Today Console.WriteLine(today.ToString("yyyy-MM-dd")) ' List(Of T) from System.Collections.Generic Dim numbers As New List(Of Integer) From {1, 2, 3} Console.WriteLine(numbers.Count) ' 3 End Sub End Module
The Imports statement brings a namespace into scope β€” equivalent to Ruby's require. System is always available, but you must import System.Collections.Generic to use List(Of T) and Dictionary(Of K, V), and System.Linq for LINQ query methods. Unlike Ruby's require, Imports is purely a compile-time alias β€” no file loading occurs.
Line Continuation
# Ruby allows implicit continuation after operators, commas, etc. result = 1 + 2 + 3 puts result
Option Strict On Imports System Module LineContinuationDemo Sub Main() ' Implicit continuation: break after operator, comma, or open paren Dim result As Integer = 1 + 2 + 3 Console.WriteLine(result) ' 6 ' Explicit continuation with underscore (legacy style β€” avoid if possible) Dim message As String = "Hello" & _ " World" Console.WriteLine(message) End Sub End Module
Modern VB.NET (since Visual Studio 2010) supports implicit line continuation after operators, commas, open parentheses, and certain keywords β€” no special character needed. The explicit underscore (_) continuation character from older VB is still valid but discouraged. Ruby and VB.NET are similar here: both allow expression spanning across lines after an operator.
Variables & Types
Basic Types
count = 42 # Integer (arbitrary precision) price = 3.14 # Float (64-bit double) greeting = "hello" # String active = true # true/false nothing = nil # nil
Option Strict On Imports System Module BasicTypesDemo Sub Main() Dim count As Integer = 42 ' 32-bit signed integer Dim price As Double = 3.14 ' 64-bit floating point Dim greeting As String = "hello" ' Unicode string Dim active As Boolean = True ' True or False Dim emptyValue As String = Nothing ' Nothing is VB's nil Console.WriteLine(count) ' 42 Console.WriteLine(price) ' 3.14 Console.WriteLine(greeting) ' hello Console.WriteLine(active) ' True Console.WriteLine(emptyValue Is Nothing) ' True End Sub End Module
VB.NET's Integer is a 32-bit signed integer (unlike Ruby's arbitrary-precision integers). Use Long for 64-bit integers. Double is the standard 64-bit floating-point type. Nothing is the VB equivalent of Ruby's nil β€” it represents the absence of a value. Boolean literals are capitalized: True and False.
Type Inference with Dim
count = 42 name = "Alice" puts count.class # Integer puts name.class # String
Option Strict On Imports System Module TypeInferenceDemo Sub Main() ' Without initializer: must declare type explicitly Dim count As Integer count = 42 ' With initializer: type can be inferred (Option Infer On, the default) Dim name = "Alice" ' inferred as String Dim price = 9.99 ' inferred as Double Console.WriteLine(count.GetType().Name) ' Int32 Console.WriteLine(name.GetType().Name) ' String Console.WriteLine(price.GetType().Name) ' Double End Sub End Module
When Option Infer On is active (the default in modern VB.NET), the compiler infers the type from the initializer β€” similar to Ruby's duck typing but resolved at compile time. Without an initializer, you must specify the type explicitly. GetType().Name returns the .NET runtime type name, analogous to Ruby's .class.
Numeric Types β€” Integer Sizes
# Ruby Integer is arbitrary precision small = 127 medium = 2_147_483_647 big = 9_223_372_036_854_775_807 puts big * 2 # No overflow β€” grows automatically
Option Strict On Imports System Module NumericTypesDemo Sub Main() Dim tiny As Byte = 255 ' 0 to 255 Dim small As Short = 32767 ' -32768 to 32767 (16-bit) Dim medium As Integer = 2147483647 ' -2^31 to 2^31-1 (32-bit) Dim big As Long = 9223372036854775807L ' -2^63 to 2^63-1 (64-bit) Dim amount As Decimal = 3.14159265358979323D ' 128-bit decimal (money) Console.WriteLine(medium) ' 2147483647 Console.WriteLine(big) ' 9223372036854775807 Console.WriteLine(amount) ' 3.14159265358979323 End Sub End Module
VB.NET provides fixed-size numeric types: Byte (8-bit), Short (16-bit), Integer (32-bit), Long (64-bit). The L suffix marks a Long literal; D marks a Decimal literal. Unlike Ruby's arbitrary-precision integers, VB.NET integers overflow silently (in release builds) or throw OverflowException (in checked context). Use Decimal for financial calculations to avoid floating-point rounding errors.
Nothing (nil)
value = nil puts value.nil? # true name = "Alice" puts name.nil? # false puts name&.upcase # "ALICE" (safe navigation)
Option Strict On Imports System Module NothingDemo Sub Main() Dim value As String = Nothing Console.WriteLine(value Is Nothing) ' True Console.WriteLine(IsNothing(value)) ' True (older style) Dim name As String = "Alice" Console.WriteLine(name Is Nothing) ' False ' Safe navigation with null check Dim upper As String = If(name IsNot Nothing, name.ToUpper(), "") Console.WriteLine(upper) ' ALICE End Sub End Module
Nothing in VB.NET is equivalent to Ruby's nil. Test for it with Is Nothing or IsNot Nothing β€” the helper function IsNothing() also works but is considered older style. VB.NET does not have Ruby's &. safe navigation operator, so null checks must be written explicitly with If().
Nullable Value Types
# Ruby's Integer is always nullable (nil is possible) count = nil puts count.nil? # true count = 42 puts count.nil? # false
Option Strict On Imports System Module NullableDemo Sub Main() ' Integer? means the value can be Nothing (Nullable(Of Integer)) Dim count As Integer? = Nothing Console.WriteLine(count.HasValue) ' False Console.WriteLine(count Is Nothing) ' True count = 42 Console.WriteLine(count.HasValue) ' True Console.WriteLine(count.Value) ' 42 ' GetValueOrDefault returns a fallback when Nothing Dim total As Integer? = Nothing Console.WriteLine(total.GetValueOrDefault(0)) ' 0 End Sub End Module
Value types like Integer and Double cannot normally be Nothing β€” they always have a value. Adding ? makes them nullable: Integer? is shorthand for Nullable(Of Integer). The .HasValue property tests whether a value is present, and .GetValueOrDefault(fallback) safely extracts the value or returns a default. Reference types like String are always nullable without the ?.
Type Conversion
puts Integer("42") # 42 puts Float("3.14") # 3.14 puts 42.to_s # "42" puts "hello".to_i # 0
Option Strict On Imports System Module TypeConversionDemo Sub Main() ' CType conversion functions Dim number As Integer = CInt("42") ' String β†’ Integer Dim fraction As Double = CDbl("3.14") ' String β†’ Double Dim text As String = CStr(42) ' Integer β†’ String Console.WriteLine(number) ' 42 Console.WriteLine(fraction) ' 3.14 Console.WriteLine(text) ' 42 ' Integer.Parse / Double.Parse for explicit parsing Dim parsed As Integer = Integer.Parse("100") Console.WriteLine(parsed) ' 100 ' TryParse for safe conversion (no exception on failure) Dim result As Integer Dim success As Boolean = Integer.TryParse("not a number", result) Console.WriteLine(success) ' False Console.WriteLine(result) ' 0 (default) End Sub End Module
VB.NET provides a family of C* conversion functions: CInt(), CDbl(), CStr(), CBool(), CLng(), etc. These throw exceptions on invalid input. For safe conversion without exceptions, use Integer.TryParse() which returns False and sets the output variable to the default value on failure β€” more explicit than Ruby's .to_i which silently returns 0.
Type Checking (TypeOf)
value = "hello" puts value.is_a?(String) # true puts value.is_a?(Integer) # false puts value.class # String
Option Strict On Imports System Module TypeCheckDemo Sub Main() Dim value As Object = "hello" Console.WriteLine(TypeOf value Is String) ' True Console.WriteLine(TypeOf value Is Integer) ' False Console.WriteLine(value.GetType().Name) ' String ' Pattern: check type and cast in one step If TypeOf value Is String Then Dim text As String = DirectCast(value, String) Console.WriteLine(text.ToUpper()) ' HELLO End If End Sub End Module
TypeOf x Is Type is VB's equivalent of Ruby's x.is_a?(Type). GetType().Name returns the runtime type name as a string. DirectCast() performs a type cast without any conversion β€” it throws InvalidCastException if the cast fails. For safer casting that returns Nothing on failure, use TryCast().
Constants
MAX_SIZE = 100 PI = 3.14159 puts MAX_SIZE # 100
Option Strict On Imports System Module ConstantsDemo Const MaxSize As Integer = 100 Const Pi As Double = 3.14159265358979 Sub Main() Console.WriteLine(MaxSize) ' 100 Console.WriteLine(Pi) ' 3.14159265358979 ' Local constant inside a procedure Const Greeting As String = "Hello" Console.WriteLine(Greeting) ' Hello End Sub End Module
The Const keyword declares a compile-time constant β€” the value must be a literal expression computable at compile time. Module-level Const declarations are shared across all methods in the module. Unlike Ruby's convention of UPPERCASE constant names, VB.NET uses PascalCase for constants by convention.
String Operations
String Interpolation
name = "Alice" age = 30 puts "Name: #{name}, Age: #{age}" puts "Next year: #{age + 1}"
Option Strict On Imports System Module InterpolationDemo Sub Main() Dim name As String = "Alice" Dim age As Integer = 30 ' Interpolated string with $ prefix (VB 14 / Visual Studio 2015+) Console.WriteLine($"Name: {name}, Age: {age}") ' Name: Alice, Age: 30 Console.WriteLine($"Next year: {age + 1}") ' Next year: 31 ' Format specifiers inside the braces Dim price As Double = 9.99 Console.WriteLine($"Price: {price:C}") ' Price: $9.99 (currency) Console.WriteLine($"Pi: {Math.PI:F4}") ' Pi: 3.1416 (4 decimal places) End Sub End Module
VB.NET uses $"..." for string interpolation, with expressions inside {} β€” the same syntax as C#. Format specifiers follow a colon inside the braces: {value:F2} for 2 decimal places, {value:C} for currency, {value:D4} for zero-padded integers. This is more powerful than Ruby's basic #{} interpolation.
String Concatenation
first = "Hello" last = "World" puts first + ", " + last # "Hello, World" puts first.concat(", ", last) # "Hello, World"
Option Strict On Imports System Module ConcatenationDemo Sub Main() Dim first As String = "Hello" Dim last As String = "World" ' & is the preferred string concatenation operator in VB.NET Console.WriteLine(first & ", " & last) ' Hello, World ' + also works when both operands are strings Console.WriteLine(first + ", " + last) ' Hello, World ' String.Concat for multiple strings Console.WriteLine(String.Concat(first, ", ", last)) ' Hello, World ' String.Join with a separator Dim words As String() = {"one", "two", "three"} Console.WriteLine(String.Join(", ", words)) ' one, two, three End Sub End Module
VB.NET uses & as the dedicated string concatenation operator β€” it always performs string concatenation regardless of operand types. The + operator also concatenates strings but is ambiguous when one operand might be numeric. String.Join(separator, array) is equivalent to Ruby's Array#join.
Common String Methods
greeting = " Hello, World! " puts greeting.strip # "Hello, World!" puts greeting.upcase # " HELLO, WORLD! " puts greeting.length # 18 puts greeting.include?("lo") # true puts greeting.gsub("l", "L") # " HeLLo, WorLd! "
Option Strict On Imports System Module StringMethodsDemo Sub Main() Dim greeting As String = " Hello, World! " Console.WriteLine(greeting.Trim()) ' "Hello, World!" Console.WriteLine(greeting.ToUpper()) ' " HELLO, WORLD! " Console.WriteLine(greeting.Length) ' 18 Console.WriteLine(greeting.Contains("lo")) ' True Console.WriteLine(greeting.Replace("l", "L")) ' " HeLLo, WorLd! " Console.WriteLine(greeting.TrimStart()) ' "Hello, World! " Console.WriteLine(greeting.StartsWith(" He")) ' True End Sub End Module
VB.NET string methods use PascalCase: Trim(), ToUpper(), ToLower(), Contains(), Replace(), StartsWith(), EndsWith(). The .Length property (not a method call β€” no parentheses) gives the character count. String comparison in VB.NET is case-sensitive by default β€” unlike older VB6 which was case-insensitive.
Substrings and Searching
text = "Hello, World!" puts text[0, 5] # "Hello" puts text.index("World") # 7 puts text[7..] # "World!"
Option Strict On Imports System Module SubstringDemo Sub Main() Dim text As String = "Hello, World!" ' Substring(startIndex, length) β€” 0-based! Console.WriteLine(text.Substring(0, 5)) ' Hello ' IndexOf returns the 0-based position (-1 if not found) Console.WriteLine(text.IndexOf("World")) ' 7 ' From position to end Console.WriteLine(text.Substring(7)) ' World! ' Left / Right / Mid from Microsoft.VisualBasic namespace (legacy style) ' Prefer Substring in modern VB.NET code Console.WriteLine(text.Substring(0, 5)) ' Hello (modern equivalent of Left) End Sub End Module
Substring(startIndex, length) is 0-based β€” unlike Ruby's bracket syntax. IndexOf() returns the 0-based position or -1 if not found, equivalent to Ruby's String#index. Classic VB functions like Left(), Right(), and Mid() are still available via the Microsoft.VisualBasic namespace but are considered legacy β€” prefer Substring() in modern VB.NET.
Split and Join
sentence = "one two three" words = sentence.split(" ") puts words.length # 3 puts words.join(", ") # "one, two, three"
Option Strict On Imports System Module SplitJoinDemo Sub Main() Dim sentence As String = "one two three" Dim words As String() = sentence.Split(" "c) Console.WriteLine(words.Length) ' 3 Console.WriteLine(words(0)) ' one ' Join an array back into a string Console.WriteLine(String.Join(", ", words)) ' one, two, three ' Split with multiple delimiters Dim csv As String = "a,b;c|d" Dim parts As String() = csv.Split(","c, ";"c, "|"c) Console.WriteLine(parts.Length) ' 4 End Sub End Module
Split() takes a Char delimiter β€” note the c suffix: " "c is a character literal, not a string. The result is a String() array, indexed from 0. String.Join(separator, array) is the inverse. To split on a string (not just a character), pass New String() {delimiter} as the argument.
Multiline Strings
message = <<~HEREDOC Line one Line two Line three HEREDOC puts message
Option Strict On Imports System Module MultilineDemo Sub Main() ' VB.NET has no heredoc β€” use & with Environment.NewLine Dim message As String = "Line one" & Environment.NewLine & "Line two" & Environment.NewLine & "Line three" Console.WriteLine(message) ' Or use vbCrLf constant (Windows line endings) Dim note As String = "First" & vbCrLf & "Second" Console.WriteLine(note) End Sub End Module
VB.NET does not have a heredoc or raw multiline string literal β€” multiline strings are built by concatenating with Environment.NewLine (platform-neutral) or the legacy constants vbCrLf (Windows \r\n), vbLf (Unix \n). C# 11 added raw string literals ("""...""") but VB.NET has not yet adopted this feature. In practice, multiline content is often loaded from a resource file or database.
Collections
Arrays
numbers = [1, 2, 3, 4, 5] puts numbers[0] # 1 puts numbers.length # 5 numbers << 6 puts numbers.length # 6
Option Strict On Imports System Module ArraysDemo Sub Main() ' Dim arr(n) creates n+1 elements (indices 0 through n) Dim numbers As Integer() = {1, 2, 3, 4, 5} Console.WriteLine(numbers(0)) ' 1 Console.WriteLine(numbers.Length) ' 5 Console.WriteLine(numbers(numbers.Length - 1)) ' 5 (last element) ' Arrays have fixed size β€” use List(Of T) for dynamic resizing Dim matrix(2, 2) As Integer ' 3Γ—3 2D array matrix(0, 0) = 1 matrix(1, 1) = 5 Console.WriteLine(matrix(1, 1)) ' 5 End Sub End Module
In VB.NET, Dim arr(n) creates an array with n+1 elements β€” indices 0 through n. This surprises many developers: Dim numbers(4) has 5 elements. Arrays have a fixed size once created; use List(Of T) for dynamic resizing. Array elements are accessed with parentheses (), not square brackets.
List(Of T) β€” Dynamic Array
fruits = ["apple", "banana", "cherry"] fruits.push("date") fruits.delete("banana") puts fruits.include?("cherry") # true puts fruits.length # 3
Option Strict On Imports System Imports System.Collections.Generic Module ListDemo Sub Main() Dim fruits As New List(Of String) From {"apple", "banana", "cherry"} fruits.Add("date") fruits.Remove("banana") Console.WriteLine(fruits.Contains("cherry")) ' True Console.WriteLine(fruits.Count) ' 3 ' Iterate For Each fruit As String In fruits Console.WriteLine(fruit) Next End Sub End Module
List(Of T) is the VB.NET equivalent of Ruby's Array β€” dynamically resizable and type-safe. Add() appends an element (like Ruby's push), Remove() removes by value, Contains() tests membership. Note .Count (not .Length) for the element count. The From {...} initializer syntax sets initial values at construction.
Dictionary(Of K, V) β€” Hash
scores = { "Alice" => 95, "Bob" => 87 } scores["Charlie"] = 92 puts scores["Alice"] # 95 puts scores.key?("Bob") # true scores.each { |name, score| puts "#{name}: #{score}" }
Option Strict On Imports System Imports System.Collections.Generic Module DictionaryDemo Sub Main() Dim scores As New Dictionary(Of String, Integer) From { {"Alice", 95}, {"Bob", 87} } scores("Charlie") = 92 Console.WriteLine(scores("Alice")) ' 95 Console.WriteLine(scores.ContainsKey("Bob")) ' True For Each entry As KeyValuePair(Of String, Integer) In scores Console.WriteLine($"{entry.Key}: {entry.Value}") Next End Sub End Module
Dictionary(Of K, V) is VB's equivalent of Ruby's Hash. Values are accessed and assigned with parentheses: scores("Alice"). ContainsKey() is equivalent to Ruby's key? or has_key?. Iterating yields KeyValuePair(Of K, V) objects with .Key and .Value properties. Accessing a missing key throws KeyNotFoundException β€” use TryGetValue() for safe access.
Array Methods
numbers = [3, 1, 4, 1, 5, 9, 2, 6] puts numbers.sort.inspect # [1, 1, 2, 3, 4, 5, 6, 9] puts numbers.min # 1 puts numbers.max # 9 puts numbers.sum # 31 puts numbers.select { |n| n > 3 }.inspect # [4, 5, 9, 6]
Option Strict On Imports System Imports System.Linq Module ArrayMethodsDemo Sub Main() Dim numbers As Integer() = {3, 1, 4, 1, 5, 9, 2, 6} ' Sort creates a sorted copy (Array.Sort mutates in place) Dim sorted As Integer() = numbers.OrderBy(Function(number) number).ToArray() Console.WriteLine(String.Join(", ", sorted)) ' 1, 1, 2, 3, 4, 5, 6, 9 Console.WriteLine(numbers.Min()) ' 1 Console.WriteLine(numbers.Max()) ' 9 Console.WriteLine(numbers.Sum()) ' 31 Dim big As Integer() = numbers.Where(Function(n) n > 3).ToArray() Console.WriteLine(String.Join(", ", big)) ' 4, 5, 9, 6 End Sub End Module
LINQ extension methods (System.Linq) provide Ruby-like higher-order operations on any collection: Min(), Max(), Sum(), Where() (filter), Select() (map), OrderBy() (sort). These return lazy sequences β€” call .ToArray() or .ToList() to materialize. Array.Sort() mutates in place, like Ruby's sort!.
HashSet(Of T) β€” Set
letters = Set.new(["a", "b", "c"]) letters.add("d") puts letters.include?("a") # true puts letters.include?("z") # false puts letters.length # 4
Option Strict On Imports System Imports System.Collections.Generic Module HashSetDemo Sub Main() Dim letters As New HashSet(Of String) From {"a", "b", "c"} letters.Add("d") letters.Add("a") ' Duplicate β€” silently ignored Console.WriteLine(letters.Contains("a")) ' True Console.WriteLine(letters.Contains("z")) ' False Console.WriteLine(letters.Count) ' 4 ' Set operations Dim other As New HashSet(Of String) From {"c", "d", "e"} letters.IntersectWith(other) Console.WriteLine(String.Join(", ", letters)) ' c, d End Sub End Module
HashSet(Of T) is VB's equivalent of Ruby's Set. Adding a duplicate element is silently ignored. It supports set operations: IntersectWith() (intersection), UnionWith() (union), ExceptWith() (difference) β€” these mutate in place. IsSubsetOf() and IsSupersetOf() are also available.
Control Flow
If / ElseIf / Else
score = 85 if score >= 90 puts "A" elsif score >= 80 puts "B" elsif score >= 70 puts "C" else puts "Below C" end
Option Strict On Imports System Module IfElseDemo Sub Main() Dim score As Integer = 85 If score >= 90 Then Console.WriteLine("A") ElseIf score >= 80 Then Console.WriteLine("B") ElseIf score >= 70 Then Console.WriteLine("C") Else Console.WriteLine("Below C") End If End Sub End Module
VB.NET uses ElseIf (one word, unlike Ruby's elsif) and closes the block with End If. Each branch condition requires the Then keyword. The overall structure is more verbose than Ruby but reads naturally in English. Single-line If condition Then statement is allowed for simple cases.
Select Case
day = "Monday" case day when "Saturday", "Sunday" then puts "Weekend" when "Monday" then puts "Start of week" else puts "Weekday" end
Option Strict On Imports System Module SelectCaseDemo Sub Main() Dim day As String = "Monday" Select Case day Case "Saturday", "Sunday" Console.WriteLine("Weekend") Case "Monday" Console.WriteLine("Start of week") Case Else Console.WriteLine("Weekday") End Select ' Select Case also supports ranges and Is comparisons Dim score As Integer = 85 Select Case score Case 90 To 100 : Console.WriteLine("A") Case 80 To 89 : Console.WriteLine("B") Case Is < 70 : Console.WriteLine("Below C") Case Else : Console.WriteLine("C") End Select End Sub End Module
Select Case is VB's equivalent of Ruby's case/when. It supports comma-separated values (Case "a", "b"), ranges (Case 1 To 10), and comparisons (Case Is > 5). The Case Else is the default branch. Unlike Ruby's case/when, VB's Select Case does not have implicit fall-through β€” only the matching case executes.
Ternary / Inline If
score = 85 grade = score >= 90 ? "Pass" : "Fail" puts grade # "Fail" # Ternary also used in assignment label = score > 50 ? "above" : "below"
Option Strict On Imports System Module TernaryDemo Sub Main() Dim score As Integer = 85 ' If() is the ternary function (not IIf β€” If() is type-safe) Dim grade As String = If(score >= 90, "Pass", "Fail") Console.WriteLine(grade) ' Fail ' Also works as null-coalescing: If(possiblyNothing, fallback) Dim name As String = Nothing Dim display As String = If(name, "anonymous") Console.WriteLine(display) ' anonymous End Sub End Module
VB.NET uses the If(condition, trueValue, falseValue) function as the ternary operator β€” not Ruby's ?: syntax. The two-argument form If(value, fallback) is a null-coalescing operator: it returns value if it is not Nothing, otherwise returns fallback. Avoid the older IIf() function β€” it is not type-safe and always evaluates both branches.
Logical Operators
# Short-circuit operators puts true && false # false puts true || false # true puts !true # false # Non-short-circuit (rare in Ruby) puts true & false # false (evaluates both sides)
Option Strict On Imports System Module LogicalDemo Sub Main() ' AndAlso / OrElse are short-circuit (preferred) Console.WriteLine(True AndAlso False) ' False Console.WriteLine(True OrElse False) ' True Console.WriteLine(Not True) ' False ' And / Or always evaluate both sides (non-short-circuit) Console.WriteLine(True And False) ' False Console.WriteLine(True Or False) ' True ' XOR Console.WriteLine(True Xor True) ' False Console.WriteLine(True Xor False) ' True End Sub End Module
VB.NET has both short-circuit operators (AndAlso, OrElse) and non-short-circuit operators (And, Or). Always prefer AndAlso and OrElse in production code β€” they skip the right operand when the result is already known, preventing null reference errors when chaining method calls. Not is the logical negation (like Ruby's !).
Loops
For...Next Loop
1.upto(5) { |index| puts index } # Step (1..10).step(2) { |index| puts index }
Option Strict On Imports System Module ForNextDemo Sub Main() For index As Integer = 1 To 5 Console.WriteLine(index) ' 1, 2, 3, 4, 5 Next ' Count backwards with Step -1 For index As Integer = 5 To 1 Step -1 Console.WriteLine(index) ' 5, 4, 3, 2, 1 Next ' Custom step For index As Integer = 0 To 10 Step 2 Console.Write(index & " ") ' 0 2 4 6 8 10 Next Console.WriteLine() End Sub End Module
For index = start To end is VB's counted loop β€” Next closes it. The Step clause sets the increment (default 1). A negative step counts downward. Unlike Ruby's block-based upto and times, the loop variable is mutable inside the loop body (though modifying it is discouraged).
For Each Loop
fruits = ["apple", "banana", "cherry"] fruits.each { |fruit| puts fruit } fruits.each_with_index do |fruit, index| puts "#{index}: #{fruit}" end
Option Strict On Imports System Imports System.Collections.Generic Module ForEachDemo Sub Main() Dim fruits As New List(Of String) From {"apple", "banana", "cherry"} For Each fruit As String In fruits Console.WriteLine(fruit) Next ' With index β€” use a counter variable Dim index As Integer = 0 For Each fruit As String In fruits Console.WriteLine($"{index}: {fruit}") index += 1 Next End Sub End Module
For Each item As Type In collection iterates over any IEnumerable β€” arrays, lists, dictionaries, and any sequence. VB.NET has no built-in equivalent of Ruby's each_with_index; use a separate counter or call .Select(Function(item, index) ...) with LINQ. The loop variable is read-only β€” modifying it does not affect the collection.
While and Do Loops
count = 0 while count < 5 count += 1 end puts count # 5 # until is the inverse until count >= 10 count += 1 end puts count # 10
Option Strict On Imports System Module WhileDoDemo Sub Main() ' While...End While β€” tests condition before each iteration Dim count As Integer = 0 While count < 5 count += 1 End While Console.WriteLine(count) ' 5 ' Do Until...Loop β€” like Ruby's until Do Until count >= 10 count += 1 Loop Console.WriteLine(count) ' 10 ' Do...Loop Until β€” tests condition AFTER first iteration (always runs once) Dim attempts As Integer = 0 Do attempts += 1 Loop Until attempts >= 3 Console.WriteLine(attempts) ' 3 End Sub End Module
VB.NET provides While...End While (test first), Do While...Loop, and Do Until...Loop (inverse). Placing the condition at the end with Do...Loop While or Do...Loop Until guarantees at least one execution β€” equivalent to Ruby's begin...end while. Unlike Ruby, VB has no until statement, but Do Until fills the same role.
Exit and Continue
(1..10).each do |index| next if index == 3 # skip 3 break if index == 7 # stop at 7 puts index end
Option Strict On Imports System Module ExitContinueDemo Sub Main() For index As Integer = 1 To 10 If index = 3 Then Continue For ' skip β€” like Ruby's next If index = 7 Then Exit For ' stop β€” like Ruby's break Console.WriteLine(index) ' 1, 2, 4, 5, 6 Next ' Exit Do inside a While loop Dim value As Integer = 0 While True value += 1 If value = 5 Then Exit While End While Console.WriteLine(value) ' 5 End Sub End Module
Continue For skips the rest of the current iteration β€” equivalent to Ruby's next. Exit For exits the loop entirely β€” equivalent to Ruby's break. The keyword always names the loop type: Exit While, Exit Do, Continue Do. VB.NET does not support labeled loops for breaking out of nested loops β€” use a Boolean flag instead.
Subroutines
Basic Subroutines
def greet(name) puts "Hello, #{name}!" end greet("Alice")
Option Strict On Imports System Module SubDemo Sub Greet(name As String) Console.WriteLine($"Hello, {name}!") End Sub Sub Main() Greet("Alice") ' Hello, Alice! End Sub End Module
A Sub is a procedure that does not return a value β€” equivalent to a Ruby method that only produces side effects. The name uses PascalCase by convention. Subroutines declared in the same Module are called without a qualifier. Unlike Ruby's methods which always return the last expression, a Sub truly returns nothing (Void in .NET terms).
ByVal and ByRef Parameters
def double_it(number) number *= 2 # Doesn't affect caller β€” Ruby passes object references by value end def double_array(items) items.map! { |item| item * 2 } # Mutates the object the reference points to end
Option Strict On Imports System Module ByValByRefDemo Sub DoubleByVal(number As Integer) ' ByVal is the default number *= 2 Console.WriteLine($"Inside: {number}") ' 20 End Sub Sub DoubleByRef(ByRef number As Integer) number *= 2 End Sub Sub Main() Dim value As Integer = 10 DoubleByVal(value) Console.WriteLine($"After ByVal: {value}") ' 10 β€” unchanged DoubleByRef(value) Console.WriteLine($"After ByRef: {value}") ' 20 β€” modified! End Sub End Module
ByVal (the default) passes a copy β€” changes inside the sub do not affect the caller's variable. ByRef passes the actual variable β€” the sub can modify the caller's value directly. This is different from Ruby's semantics: Ruby always passes object references by value (you can mutate the object but can't rebind the caller's variable). ByRef is equivalent to a true C-style pointer parameter.
Optional and Default Parameters
def greet(name, greeting = "Hello") puts "#{greeting}, #{name}!" end greet("Alice") # "Hello, Alice!" greet("Bob", "Hi") # "Hi, Bob!"
Option Strict On Imports System Module OptionalDemo Sub Greet(name As String, Optional greeting As String = "Hello") Console.WriteLine($"{greeting}, {name}!") End Sub Sub Main() Greet("Alice") ' Hello, Alice! Greet("Bob", "Hi") ' Hi, Bob! ' Named arguments β€” can pass in any order Greet(greeting:="Hey", name:="Charlie") ' Hey, Charlie! End Sub End Module
VB.NET uses the Optional keyword with a default value specified via = value. Named arguments use := syntax and can be passed in any order β€” useful when skipping optional parameters in the middle of a parameter list. This is identical in concept to Ruby's default arguments and keyword arguments.
ParamArray (Splat)
def sum(*numbers) numbers.sum end puts sum(1, 2, 3) # 6 puts sum(10, 20, 30) # 60
Option Strict On Imports System Module ParamArrayDemo Function Sum(ParamArray numbers As Integer()) As Integer Dim total As Integer = 0 For Each number As Integer In numbers total += number Next Return total End Function Sub Main() Console.WriteLine(Sum(1, 2, 3)) ' 6 Console.WriteLine(Sum(10, 20, 30)) ' 60 ' Can also pass an array Dim values As Integer() = {5, 10, 15} Console.WriteLine(Sum(values)) ' 30 End Sub End Module
ParamArray is VB's equivalent of Ruby's splat (*args) β€” it collects all remaining arguments into an array. It must be the last parameter, and the type must be a one-dimensional array. Unlike Ruby's splat, you cannot have additional required parameters after a ParamArray parameter.
Functions
Functions with Return Values
def square(number) number * number # last expression is returned end puts square(5) # 25
Option Strict On Imports System Module FunctionDemo Function Square(number As Integer) As Integer Return number * number End Function ' Legacy style: assign to the function name instead of Return Function Cube(number As Integer) As Integer Cube = number * number * number End Function Sub Main() Console.WriteLine(Square(5)) ' 25 Console.WriteLine(Cube(3)) ' 27 End Sub End Module
A Function differs from a Sub by declaring a return type after As and using Return. The legacy style assigns to the function's own name instead of using Return β€” avoid this in modern VB.NET as it is confusing. Unlike Ruby, the return type is declared explicitly, and the Return statement is required.
Recursive Functions
def factorial(number) return 1 if number <= 1 number * factorial(number - 1) end puts factorial(5) # 120
Option Strict On Imports System Module RecursionDemo Function Factorial(number As Integer) As Long If number <= 1 Then Return 1L Return CLng(number) * Factorial(number - 1) End Function Sub Main() Console.WriteLine(Factorial(5)) ' 120 Console.WriteLine(Factorial(10)) ' 3628800 End Sub End Module
VB.NET supports recursion without any special keyword β€” a Function can call itself directly. Using Long as the return type avoids integer overflow for larger factorial values. Unlike Ruby, VB.NET does not optimize tail calls in most contexts, so very deep recursion will throw a StackOverflowException β€” convert to an iterative approach for large inputs.
Lambda Expressions
double = ->(number) { number * 2 } puts double.call(5) # 10 add = ->(a, b) { a + b } puts add.call(3, 4) # 7
Option Strict On Imports System Module LambdaDemo Sub Main() ' Function lambda (returns a value) Dim doubler As Func(Of Integer, Integer) = Function(number) number * 2 Console.WriteLine(doubler(5)) ' 10 ' Two-parameter lambda Dim add As Func(Of Integer, Integer, Integer) = Function(x, y) x + y Console.WriteLine(add(3, 4)) ' 7 ' Action lambda (returns nothing β€” like Sub) Dim greet As Action(Of String) = Sub(name) Console.WriteLine($"Hello, {name}!") greet("Alice") ' Hello, Alice! ' Multi-line lambda Dim classify As Func(Of Integer, String) = Function(score) If score >= 90 Then Return "A" If score >= 80 Then Return "B" Return "C" End Function Console.WriteLine(classify(85)) ' B End Sub End Module
VB.NET lambdas use Function(params) expression for value-returning lambdas and Sub(params) expression for void lambdas. Multi-statement lambdas use the Function(params) ... End Function form. The type is expressed as Func(Of T1, T2, TReturn) or Action(Of T) β€” .NET delegate types from the System namespace.
Higher-Order Functions
numbers = [1, 2, 3, 4, 5] puts numbers.map { |n| n * 2 }.inspect # [2, 4, 6, 8, 10] puts numbers.select { |n| n.even? }.inspect # [2, 4] puts numbers.reduce(0) { |sum, n| sum + n } # 15
Option Strict On Imports System Imports System.Linq Module HigherOrderDemo Sub Main() Dim numbers As Integer() = {1, 2, 3, 4, 5} ' Select (map) β€” transform each element Dim doubled = numbers.Select(Function(number) number * 2).ToArray() Console.WriteLine(String.Join(", ", doubled)) ' 2, 4, 6, 8, 10 ' Where (filter) β€” keep matching elements Dim even = numbers.Where(Function(number) number Mod 2 = 0).ToArray() Console.WriteLine(String.Join(", ", even)) ' 2, 4 ' Aggregate (reduce) β€” fold into a single value Dim total = numbers.Aggregate(0, Function(sum, number) sum + number) Console.WriteLine(total) ' 15 End Sub End Module
LINQ provides Select() (Ruby's map), Where() (Ruby's select), and Aggregate() (Ruby's reduce). These accept lambdas and return lazy sequences. ToArray() or ToList() materializes the result. The naming difference β€” Select for map β€” is a common source of confusion for Rubyists.
Classes & OOP
Basic Class
class Animal def initialize(name, sound) @name = name @sound = sound end def speak puts "#{@name} says #{@sound}!" end end dog = Animal.new("Dog", "Woof") dog.speak
Option Strict On Imports System Module ClassDemo Class Animal Private name As String Private sound As String Sub New(name As String, sound As String) Me.name = name Me.sound = sound End Sub Sub Speak() Console.WriteLine($"{name} says {sound}!") End Sub End Class Sub Main() Dim dog As New Animal("Dog", "Woof") dog.Speak() ' Dog says Woof! End Sub End Module
VB.NET classes use Sub New() as the constructor β€” equivalent to Ruby's initialize. Me is VB's equivalent of Ruby's self and is used to disambiguate when a parameter name shadows an instance field. Instances are created with New ClassName(args). Fields declared with Private are not accessible outside the class.
Properties
class Person attr_reader :name attr_accessor :age def initialize(name, age) @name = name @age = age end end person = Person.new("Alice", 30) puts person.name # "Alice" person.age = 31 puts person.age # 31
Option Strict On Imports System Module PropertiesDemo Class Person ' Auto-implemented property (read/write) Public Property Age As Integer ' Read-only property Private _name As String Public ReadOnly Property Name As String Get Return _name End Get End Property Sub New(name As String, age As Integer) _name = name Me.Age = age End Sub End Class Sub Main() Dim person As New Person("Alice", 30) Console.WriteLine(person.Name) ' Alice person.Age = 31 Console.WriteLine(person.Age) ' 31 End Sub End Module
VB.NET properties provide controlled access to fields. A Property with only a Get block and the ReadOnly modifier is equivalent to Ruby's attr_reader. An auto-implemented property (Public Property Age As Integer) generates a backing field automatically β€” equivalent to Ruby's attr_accessor. The WriteOnly modifier creates a write-only property.
Inheritance
class Animal def initialize(name) @name = name end def speak puts "..." end end class Dog < Animal def speak puts "#{@name} says Woof!" end end Dog.new("Rex").speak
Option Strict On Imports System Module InheritanceDemo Class Animal Protected name As String Sub New(name As String) Me.name = name End Sub Overridable Sub Speak() Console.WriteLine("...") End Sub End Class Class Dog Inherits Animal Sub New(name As String) MyBase.New(name) End Sub Overrides Sub Speak() Console.WriteLine($"{name} says Woof!") End Sub End Class Sub Main() Dim dog As New Dog("Rex") dog.Speak() ' Rex says Woof! End Sub End Module
Inherits declares the base class (equivalent to Ruby's <). Methods must be explicitly marked Overridable to allow overriding β€” unlike Ruby where all methods are overridable by default. The subclass marks its override with Overrides. MyBase.New() calls the parent constructor, equivalent to Ruby's super. Protected makes a member accessible to subclasses.
Interfaces
module Speakable def speak raise NotImplementedError end end class Cat include Speakable def speak puts "Meow!" end end Cat.new.speak
Option Strict On Imports System Module InterfacesDemo Interface ISpeakable Sub Speak() Function Describe() As String End Interface Class Cat Implements ISpeakable Sub Speak() Implements ISpeakable.Speak Console.WriteLine("Meow!") End Sub Function Describe() As String Implements ISpeakable.Describe Return "I am a cat" End Function End Class Sub Main() Dim animal As ISpeakable = New Cat() animal.Speak() ' Meow! Console.WriteLine(animal.Describe()) ' I am a cat End Sub End Module
VB.NET interfaces are similar to Ruby modules used as mixins, but enforced at compile time. Interface declares the contract; Implements declares adherence. Each implemented member must explicitly name the interface method it satisfies (Implements ISpeakable.Speak). A class can implement multiple interfaces, allowing polymorphism without multiple inheritance.
Shared Members (Class Methods)
class Counter @@count = 0 def self.increment @@count += 1 end def self.count @@count end end Counter.increment Counter.increment puts Counter.count # 2
Option Strict On Imports System Module SharedMembersDemo Class Counter Private Shared _count As Integer = 0 Shared Sub Increment() _count += 1 End Sub Shared ReadOnly Property Count As Integer Get Return _count End Get End Property End Class Sub Main() Counter.Increment() Counter.Increment() Console.WriteLine(Counter.Count) ' 2 End Sub End Module
Shared in VB.NET is equivalent to Ruby's self. methods and class variables β€” the member belongs to the class itself, not to instances. A Shared method is called on the class name directly. Shared fields are global state shared across all instances, analogous to Ruby's @@class_variables.
Error Handling
Try / Catch / Finally
begin result = 10 / 0 rescue ZeroDivisionError => error puts "Caught: #{error.message}" ensure puts "Always runs" end
Option Strict On Imports System Module TryCatchDemo Sub Main() Try Dim result As Integer = Integer.Parse("not a number") Console.WriteLine(result) Catch ex As FormatException Console.WriteLine($"Caught: {ex.Message}") Catch ex As Exception Console.WriteLine($"General error: {ex.Message}") Finally Console.WriteLine("Always runs") ' Always runs End Try End Sub End Module
VB.NET uses Try...Catch...Finally...End Try β€” parallel to Ruby's begin...rescue...ensure...end. Multiple Catch blocks handle different exception types (most specific first). The Finally block always runs, even if an exception is thrown and not caught. The catch variable name (here ex) is a local alias for the caught exception β€” equivalent to Ruby's rescue SomeError => ex.
Custom Exceptions
class InsufficientFundsError < StandardError def initialize(amount) super("Cannot withdraw #{amount}: insufficient funds") end end begin raise InsufficientFundsError.new(100) rescue InsufficientFundsError => ex puts ex.message end
Option Strict On Imports System Module CustomExceptionDemo Class InsufficientFundsException Inherits Exception Sub New(amount As Decimal) MyBase.New($"Cannot withdraw {amount:C}: insufficient funds") End Sub End Class Sub Withdraw(balance As Decimal, amount As Decimal) If amount > balance Then Throw New InsufficientFundsException(amount) End If Console.WriteLine($"Withdrew {amount:C}") End Sub Sub Main() Try Withdraw(50D, 100D) Catch ex As InsufficientFundsException Console.WriteLine(ex.Message) End Try End Sub End Module
Custom exceptions inherit from Exception (or a more specific subclass) β€” equivalent to Ruby's pattern of inheriting from StandardError. The Throw statement raises an exception (Ruby's raise). MyBase.New(message) passes the message to the base class constructor. By convention, custom exception class names end in Exception.
Re-raising Exceptions
def process begin raise "Something failed" rescue => error puts "Logging: #{error.message}" raise # Re-raise the same exception end end begin process rescue => error puts "Outer: #{error.message}" end
Option Strict On Imports System Module RethrowDemo Sub Process() Try Throw New Exception("Something failed") Catch ex As Exception Console.WriteLine($"Logging: {ex.Message}") Throw ' Re-raise preserving original stack trace End Try End Sub Sub Main() Try Process() Catch ex As Exception Console.WriteLine($"Outer: {ex.Message}") End Try End Sub End Module
A bare Throw inside a Catch block re-raises the current exception while preserving its original stack trace β€” equivalent to Ruby's bare raise. Using Throw error (with the exception variable) re-throws but resets the stack trace to the current line β€” avoid this pattern when you want to preserve the original call stack for debugging.
LINQ
LINQ Query Syntax
products = [ { name: "Apple", price: 1.5 }, { name: "Banana", price: 0.5 }, { name: "Cherry", price: 3.0 }, ] cheap = products.select { |p| p[:price] < 2 } .map { |p| p[:name] } puts cheap.inspect # ["Apple", "Banana"]
Option Strict On Imports System Imports System.Linq Module LinqQueryDemo Structure Product Public Name As String Public Price As Double Sub New(name As String, price As Double) Me.Name = name Me.Price = price End Sub End Structure Sub Main() Dim products As Product() = { New Product("Apple", 1.5), New Product("Banana", 0.5), New Product("Cherry", 3.0) } ' SQL-like query syntax Dim cheapNames = From product In products Where product.Price < 2.0 Select product.Name For Each name As String In cheapNames Console.WriteLine(name) ' Apple, Banana Next End Sub End Module
LINQ's query syntax reads like SQL: From...Where...Select. It compiles to the same method calls as the fluent syntax (.Where().Select()) β€” choose whichever reads more clearly. The query is lazy: it executes when iterated with For Each or materialized with .ToList(). This is more declarative than Ruby's chained block methods.
LINQ Method Syntax
numbers = [5, 2, 8, 1, 9, 3, 7, 4, 6] result = numbers.select { |n| n > 3 } .sort .take(3) puts result.inspect # [4, 5, 6]
Option Strict On Imports System Imports System.Linq Module LinqMethodDemo Sub Main() Dim numbers As Integer() = {5, 2, 8, 1, 9, 3, 7, 4, 6} Dim result = numbers.Where(Function(number) number > 3) _ .OrderBy(Function(number) number) _ .Take(3) _ .ToArray() Console.WriteLine(String.Join(", ", result)) ' 4, 5, 6 ' Group by Dim grouped = numbers.GroupBy(Function(number) If(number > 5, "big", "small")) For Each group In grouped Console.WriteLine($"{group.Key}: {String.Join(", ", group)}") Next End Sub End Module
LINQ method syntax chains extension methods β€” Where(), OrderBy(), Take(), Skip(), GroupBy(), Distinct(). Each returns an IEnumerable for further chaining. Take(n) is Ruby's first(n); Skip(n) is Ruby's drop(n). GroupBy() returns groups of IGrouping objects with a .Key property.
LINQ Aggregates
numbers = [3, 1, 4, 1, 5, 9, 2, 6] puts numbers.count # 8 puts numbers.sum # 31 puts numbers.min # 1 puts numbers.max # 9 puts numbers.sum.to_f / numbers.count # 3.875
Option Strict On Imports System Imports System.Linq Module LinqAggregateDemo Sub Main() Dim numbers As Integer() = {3, 1, 4, 1, 5, 9, 2, 6} Console.WriteLine(numbers.Count()) ' 8 Console.WriteLine(numbers.Sum()) ' 31 Console.WriteLine(numbers.Min()) ' 1 Console.WriteLine(numbers.Max()) ' 9 Console.WriteLine(numbers.Average()) ' 3.875 Console.WriteLine(numbers.Distinct().Count()) ' 7 (unique values) ' First / Last / Any / All Console.WriteLine(numbers.First()) ' 3 Console.WriteLine(numbers.Any(Function(n) n > 8)) ' True Console.WriteLine(numbers.All(Function(n) n > 0)) ' True End Sub End Module
LINQ aggregates provide Count(), Sum(), Min(), Max(), and Average() β€” these work like Ruby's equivalently named Enumerable methods. Distinct() removes duplicates (Ruby's uniq). Any(predicate) is Ruby's any? and All(predicate) is Ruby's all?.
Modules & Namespaces
Modules vs Classes
module MathUtils def self.square(number) number * number end end puts MathUtils.square(5) # 25
Option Strict On Imports System ' A Module in VB.NET is a sealed class with only Shared members Module MathUtils Function Square(number As Integer) As Integer Return number * number End Function Function Cube(number As Integer) As Integer Return number * number * number End Function End Module Module Main Sub Main() ' Module members are accessed without an instance Console.WriteLine(MathUtils.Square(5)) ' 25 Console.WriteLine(MathUtils.Cube(3)) ' 27 End Sub End Module
A VB.NET Module is a sealed class whose members are implicitly Shared β€” you cannot create instances of it. This is similar to a Ruby module used only for module_function methods. Unlike Ruby modules, VB.NET modules cannot be mixed into classes. They are best used for utility functions and the program entry point.
Namespaces
module Geometry module Shapes class Circle def initialize(radius) @radius = radius end def area = Math::PI * @radius ** 2 end end end circle = Geometry::Shapes::Circle.new(5) puts circle.area.round(2) # 78.54
Option Strict On Imports System Namespace Geometry.Shapes Class Circle Private radius As Double Sub New(radius As Double) Me.radius = radius End Sub Function Area() As Double Return Math.PI * radius * radius End Function End Class End Namespace Module NamespaceDemo Sub Main() Dim circle As New Geometry.Shapes.Circle(5.0) Console.WriteLine(Math.Round(circle.Area(), 2)) ' 78.54 End Sub End Module
Namespace organizes types hierarchically β€” equivalent to Ruby's nested modules. Dots in the namespace name (Geometry.Shapes) are shorthand for nested namespaces. The Imports statement at the top of a file brings a namespace into scope so you can write Circle instead of Geometry.Shapes.Circle.
⚠ Gotchas for Rubyists
Dim arr(n) Creates n+1 Elements
# Ruby: Array.new(5) creates 5 elements (indices 0-4) numbers = Array.new(5, 0) puts numbers.length # 5 puts numbers.last # 0
Option Strict On Imports System Module DimGotchaDemo Sub Main() ' Dim numbers(4) creates 5 elements β€” indices 0 THROUGH 4! ' The argument is the UPPER BOUND, not the count. Dim numbers(4) As Integer Console.WriteLine(numbers.Length) ' 5 β€” NOT 4! Console.WriteLine(numbers(0)) ' 0 (default) Console.WriteLine(numbers(4)) ' 0 (last valid index) ' To create an array with exactly N elements, use N-1 as the bound Dim tenElements(9) As Integer Console.WriteLine(tenElements.Length) ' 10 End Sub End Module
This is one of the most common VB.NET mistakes for newcomers: Dim arr(n) creates an array with n+1 elements (indices 0 to n), because the argument is the upper bound index, not the count. To create an array with exactly 10 elements, write Dim arr(9). This legacy comes from original BASIC, where DIM A(10) created 11 elements (0 through 10).
Case-Insensitive Language
# Ruby is case-sensitive β€” these are three different things message = "hello" Message = "different variable" # Constant (uppercase) # MESSAGE would be a third constant
Option Strict On Imports System Module CaseInsensitiveDemo Sub Main() ' VB.NET is case-insensitive β€” these are all the same variable Dim message As String = "hello" MESSAGE = "overwritten" ' Same variable as message! Console.WriteLine(Message) ' overwritten ' Keyword casing doesn't matter either DIM value AS INTEGER = 42 CONSOLE.WRITELINE(value) ' 42 End Sub End Module
VB.NET is entirely case-insensitive β€” message, Message, and MESSAGE all refer to the same identifier. This is the opposite of Ruby, where case is meaningful: name is a local variable and Name is a constant. The VB.NET convention uses PascalCase for public members and camelCase (or an underscore prefix) for private fields, but these are just conventions β€” the compiler treats them identically.
Integer Division with Backslash
puts 7 / 2 # 3 (integer division β€” Ruby infers from operand types) puts 7.0 / 2 # 3.5 puts 7 / 2.0 # 3.5
Option Strict On Imports System Module DivisionDemo Sub Main() ' \ is integer division (truncates toward zero) Console.WriteLine(7 \ 2) ' 3 ' / is always floating-point division for Doubles Dim a As Double = 7.0 Console.WriteLine(a / 2) ' 3.5 ' / between two Integers returns Double in VB.NET! Dim x As Integer = 7 Dim y As Integer = 2 Console.WriteLine(x / y) ' 3.5 (surprising!) Console.WriteLine(x \ y) ' 3 (integer truncation) ' Mod for remainder Console.WriteLine(7 Mod 2) ' 1 End Sub End Module
VB.NET's / operator returns Double even when dividing two integers β€” this is the opposite of Ruby, where 7 / 2 returns 3. For integer division, use the backslash \ operator. This surprises Rubyists expecting 7 / 2 to give 3. The Mod keyword (not %) computes the remainder.
String Comparison β€” Case Sensitivity
# Ruby: case-sensitive by default puts "Hello" == "hello" # false puts "Hello".casecmp?("hello") # true (case-insensitive compare)
Option Strict On Imports System Module StringCompareDemo Sub Main() ' Default: Ordinal (case-sensitive binary comparison) Console.WriteLine("Hello" = "hello") ' False Console.WriteLine(String.Equals("Hello", "hello", StringComparison.OrdinalIgnoreCase)) ' True ' Select Case with strings is case-sensitive by default ' Add "Option Compare Text" at the top of the file to make it case-insensitive Select Case "HELLO" Case "hello" Console.WriteLine("case-insensitive match") Case "HELLO" Console.WriteLine("exact match") ' This executes End Select End Sub End Module
Despite VB.NET being a case-insensitive language (for identifiers), string comparisons with = are case-sensitive by default (Ordinal binary comparison). This catches many Rubyists who assume VB's case-insensitivity extends to string data. Use String.Equals(a, b, StringComparison.OrdinalIgnoreCase) for case-insensitive string comparison. Adding Option Compare Text at the top of a file makes all string comparisons in that file case-insensitive.
Nothing vs nil β€” Subtle Differences
puts nil.nil? # true puts nil.class # NilClass # nil has methods β€” it's an object puts nil.to_s # "" (empty string) puts nil.to_a # []
Option Strict On Imports System Module NothingVsNilDemo Sub Main() ' Nothing for reference types: behaves like null Dim text As String = Nothing Console.WriteLine(text Is Nothing) ' True ' Console.WriteLine(text.Length) ' NullReferenceException! ' Nothing for value types: becomes the default value (NOT null) Dim count As Integer = Nothing Console.WriteLine(count) ' 0 β€” NOT null, just the default! ' Safe access using null check Dim upper As String = If(text IsNot Nothing, text.ToUpper(), "(missing)") Console.WriteLine(upper) ' (missing) End Sub End Module
Nothing is not the same as Ruby's nil: assigned to a value type (Integer, Double), it becomes the type's default (0, 0.0, False) β€” not a null. It only truly means "absent" for reference types and nullable value types (Integer?). Calling a method on a Nothing reference type throws NullReferenceException β€” unlike Ruby where calling methods on nil raises NoMethodError (descriptively).
And vs AndAlso β€” Non-Short-Circuit Trap
# Ruby's && always short-circuits β€” right side not evaluated if left is false user = nil puts user && user.name # nil (safe β€” user.name not called)
Option Strict On Imports System Module AndGotchaDemo Class User Property Name As String = "Alice" End Class Sub Main() Dim user As User = Nothing ' SAFE: AndAlso short-circuits β€” right side not evaluated If user IsNot Nothing AndAlso user.Name = "Alice" Then Console.WriteLine("Found Alice") End If Console.WriteLine("No crash with AndAlso") ' DANGEROUS: And evaluates both sides β€” crashes here! Try If user IsNot Nothing And user.Name = "Alice" Then ' NullReferenceException! Console.WriteLine("Found Alice") End If Catch ex As NullReferenceException Console.WriteLine("Crash! And is not short-circuit") End Try End Sub End Module
Using And instead of AndAlso is a common VB.NET bug β€” And always evaluates both sides, causing NullReferenceException when the right side references an object that is Nothing. Ruby's && always short-circuits, so Rubyists switching to VB.NET must actively remember to use AndAlso and OrElse instead of And and Or.