Core VB.NET patterns, type safety, modern language features
user-invocable
false
disable-model-invocation
true
version
1.0.0
category
toolchain
author
Claude MPM Team
license
MIT
tags
["visualbasic","vb.net","dotnet","type-safety"]
Visual Basic .NET Core Patterns
Modern VB.NET (2019+) best practices with focus on type safety, LINQ, async/await, and .NET integration.
Quick Start
' Always enable strict type checkingOptionStrictOnOptionExplicitOnOption Infer On' Modern class definitionPublicClass Customer
PublicProperty Id AsIntegerPublicProperty Name AsStringPublicProperty Email AsString' ConstructorPublicSubNew(id AsInteger, name AsString, email AsString)
Me.Id = id
Me.Name = name
Me.Email = email
EndSubEndClass' Modern async methodPublicAsyncFunction GetCustomerAsync(id AsInteger) As Task(Of Customer)
Dim result = Await database.QueryAsync( Customer)(
,
{.id = id}
)
result.FirstOrDefault()
activeCustomers = c customers
c.IsActive c.Balance >
c.Name
c
Of
"SELECT * FROM Customers WHERE Id = @id"
New
With
Return
End
Function
' LINQ query
Dim
From
In
Where
AndAlso
0
Order
By
Select
Type Safety (Critical)
Option Strict On
' ALWAYS at top of every fileOptionStrictOnOptionExplicitOnOption Infer On' Why:' - Option Strict: Prevents implicit narrowing conversions' - Option Explicit: Requires variable declaration' - Option Infer: Enables type inference (but still type-safe)
Explicit Types vs Inference
' ✅ Good: Explicit when clarity helpsDim customerName AsString = GetCustomerName(id)
Dim count AsInteger = GetCount()
' ✅ Good: Inference when type is obviousDim customer = New Customer(1, "John", "john@example.com")
Dim items = New List(OfString) From {"a", "b", "c"}
' ❌ Bad: Dim without type and no inferenceDim data = GetData() ' What type is data?' ✅ Better: Be explicitDim data As DataTable = GetData()
Nullable Types
' Modern nullable syntax (VB 15.5+)Dim middleName AsString? = NothingDim age AsInteger? = Nothing' Null-conditional operatorDim length AsInteger? = middleName?.Length
' Null-coalescing operatorDim displayName AsString = middleName IfNothing' Check for nullIf age IsNotNothingThen
Console.WriteLine($"Age: {age.Value}")
EndIf' GetValueOrDefaultDim years AsInteger = age.GetValueOrDefault(0)
Modern Language Features
String Interpolation
' ✅ Modern (VB 14+)Dim message = $"Customer {customer.Name} has balance {customer.Balance:C}"' ❌ Old style (avoid)Dim message = String.Format("Customer {0} has balance {1:C}", customer.Name, customer.Balance)
' ❌ Concatenation (avoid for complex strings)Dim message = "Customer " & customer.Name & " has balance " & customer.Balance.ToString("C")
' FilteringDim activeCustomers = customers.Where(Function(c) c.IsActive).ToList()
' ProjectionDim names = customers.Select(Function(c) c.Name).ToList()
' OrderingDim sorted = customers.OrderBy(Function(c) c.Name).ThenBy(Function(c) c.Id)
' GroupingDim grouped = From c In customers
Group c By c.City IntoGroupSelect City, Customers = Group' AggregationDim total = orders.Sum(Function(o) o.Amount)
Dim average = orders.Average(Function(o) o.Amount)
Dim count = customers.Count(Function(c) c.IsActive)
' First/SingleDim first = customers.FirstOrDefault(Function(c) c.Id = 1)
Dimsingle = customers.SingleOrDefault(Function(c) c.Email = email)
' Any/AllDim hasActive = customers.Any(Function(c) c.IsActive)
Dim allActive = customers.All(Function(c) c.IsActive)
Async/Await Patterns
Async Method Declaration
' Return Task(Of T) for async methods with return valuePublicAsyncFunction GetDataAsync() As Task(OfString)
Dim result = Await httpClient.GetStringAsync(url)
Return result
EndFunction' Return Task for async methods without return valuePublicAsyncFunction SaveDataAsync(data AsString) As Task
Await File.WriteAllTextAsync(filePath, data)
EndFunction' Async Sub only for event handlersPrivateAsyncSub Button_Click(sender AsObject, e As EventArgs) Handles Button.Click
Await ProcessDataAsync()
EndSub
Async Best Practices
' ✅ Good: Await all the wayPublicAsyncFunction ProcessOrderAsync(orderId AsInteger) As Task(OfBoolean)
Dimorder = Await GetOrderAsync(orderId)
Await ValidateOrderAsync(order)
Await SaveOrderAsync(order)
ReturnTrueEndFunction' ❌ Bad: Blocking on async (deadlock risk)PublicFunction ProcessOrder(orderId AsInteger) AsBooleanDimorder = GetOrderAsync(orderId).Result ' Can deadlock!ReturnTrueEndFunction' ✅ Good: ConfigureAwait(False) in librariesPublicAsyncFunction GetDataAsync() As Task(OfString)
Dim result = Await httpClient.GetStringAsync(url).ConfigureAwait(False)
Return result
EndFunction' Parallel async operationsDim tasks = New List(Of Task(Of Customer)) From {
GetCustomerAsync(1),
GetCustomerAsync(2),
GetCustomerAsync(3)
}
Dim customers = Await Task.WhenAll(tasks)
Error Handling
Try-Catch Patterns
' Modern structured exception handlingTryDim result = Await ProcessDataAsync()
Return result
Catch ex As ArgumentNullException
' Handle specific exception
logger.LogError(ex, "Null argument in ProcessData")
ThrowCatch ex As HttpRequestException
' Handle another specific exception
logger.LogError(ex, "HTTP request failed")
ReturnNothingCatch ex As Exception
' Catch-all (use sparingly)
logger.LogError(ex, "Unexpected error")
ThrowFinally' Cleanup (always runs)
connection?.Dispose()
EndTry
Exception Filters
' VB 14+ exception filtersTry
ProcessData()
Catch ex As IOException When ex.Message.Contains("file not found")
' Handle specific IO error
CreateDefaultFile()
Catch ex As IOException When ex.Message.Contains("access denied")
' Handle different IO error
RequestPermissions()
EndTry
' Simple auto propertyPublicProperty Name AsString' With default valuePublicProperty IsActive AsBoolean = True' Read-only auto property (VB 14+)PublicReadOnlyProperty Id AsInteger' Can only be set in constructorPublicSubNew(id AsInteger)
Me.Id = id
EndSub
Computed Properties
PublicClass Customer
PublicProperty FirstName AsStringPublicProperty LastName AsString' Computed propertyPublicReadOnlyProperty FullName AsStringGetReturn $"{FirstName} {LastName}"EndGetEndProperty' Property with validationPrivate _age AsIntegerPublicProperty Age AsIntegerGetReturn _age
EndGetSet(value AsInteger)
If value < 0Or value > 150ThenThrowNew ArgumentOutOfRangeException(NameOf(Age))
EndIf
_age = value
EndSetEndPropertyEndClass
Interfaces and Inheritance
Interface Definition
PublicInterface IRepository(Of T)
Function GetByIdAsync(id AsInteger) As Task(Of T)
Function GetAllAsync() As Task(Of IEnumerable(Of T))
Function AddAsync(entity As T) As Task
Function UpdateAsync(entity As T) As Task
Function DeleteAsync(id AsInteger) As Task
EndInterface' ImplementationPublicClass CustomerRepository
Implements IRepository(Of Customer)
PublicAsyncFunction GetByIdAsync(id AsInteger) As Task(Of Customer) _
Implements IRepository(Of Customer).GetByIdAsync
ReturnAwait dbContext.Customers.FindAsync(id)
EndFunction' ... other implementationsEndClass
Class Inheritance
' Base classPublicMustInheritClass BaseEntity
PublicProperty Id AsIntegerPublicProperty CreatedAt As DateTime
PublicProperty UpdatedAt As DateTime
PublicMustOverrideSub Validate()
EndClass' Derived classPublicClass Customer
Inherits BaseEntity
PublicProperty Name AsStringPublicProperty Email AsStringPublicOverridesSub Validate()
IfString.IsNullOrWhiteSpace(Name) ThenThrowNew ValidationException("Name is required")
EndIfIfNot Email.Contains("@") ThenThrowNew ValidationException("Invalid email format")
EndIfEndSubEndClass
Best Practices
✅ DO
' Always use Option Strict OnOptionStrictOnOptionExplicitOnOption Infer On' Use modern syntax (LINQ, async/await, string interpolation)Dim activeCustomers = Await customers.Where(Function(c) c.IsActive).ToListAsync()
Dim message = $"Found {activeCustomers.Count} active customers"' Use meaningful namesDim customerEmailAddress AsStringDim isCustomerActive AsBoolean' Use async/await for I/O operationsPublicAsyncFunction LoadDataAsync() As Task(Of Data)
' Use IDisposable patternUsing connection = New SqlConnection(connectionString)
' Use connectionEndUsing' Use XML comments for public APIs''' <summary>''' Gets a customer by their unique identifier.''' </summary>''' <param name="customerId">The customer ID to search for.</param>''' <returns>The customer if found, Nothing otherwise.</returns>PublicAsyncFunction GetCustomerAsync(customerId AsInteger) As Task(Of Customer)
❌ DON'T
' Don't use On Error (use Try-Catch)OnErrorResumeNext' Legacy VB6 style - AVOID' Don't use late bindingDim obj AsObject = CreateObject("Excel.Application") ' Use typed references' Don't block on asyncDim result = GetDataAsync().Result ' Can deadlock' Don't use underscores in new code (legacy convention)Private m_customerName AsString' Use camelCase: _customerName' Don't concatenate strings in loopsDim result AsString = ""ForEach item In items
result &= item ' Use StringBuilder or String.JoinNext' Don't use Hungarian notationDim strName AsString' Just use: Dim name As StringDim intCount AsInteger' Just use: Dim count As Integer
Related Skills
When working with VB.NET core patterns, these skills enhance your workflow:
vb-winforms: Windows Forms development patterns
vb-database: ADO.NET and database integration patterns
testing-anti-patterns: Testing best practices for VB.NET
Remember
Option Strict On is non-negotiable for type safety
Use modern .NET features (LINQ, async/await, tuples)
Avoid legacy VB6 patterns (On Error, late binding)
Follow .NET naming conventions (PascalCase for public, camelCase for private)