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.