变量、类型与运算符
变量声明与内置类型
VB.NET 是静态类型语言,使用 Dim 声明。常见类型:String、Integer(32 位)、Long(64 位)、Double(64 位浮点)、Decimal(金融高精度)、Boolean、Date、Char。使用类型后缀(D 表示 Decimal,F 表示 Single,L 表示 Long)强制字面量类型。Option Explicit On 强制变量声明(防止拼写错误)。Option Infer On 启用类型推断,无需显式类型即可使用 Dim。可空类型(Integer?)包装值类型,使其可以持有 Nothing —— 对数据库和 API 很有用。
' Option Explicit On -- requires declaration (recommended)
Module Variables
Sub Main()
Dim name As String = "Alice" ' text
Dim age As Integer = 30 ' 32-bit integer
Dim salary As Decimal = 50000.50D ' precise money
Dim pi As Double = 3.14159 ' 64-bit float
Dim isDev As Boolean = True ' True/False
Dim letter As Char = "A"c ' single character
Dim today As Date = #2024-06-18# ' date literal
' type inference (Option Infer On)
Dim count = 10 ' inferred as Integer
Dim message = "Hello" ' inferred as String
' nullable types (Value types that can be Nothing)
Dim score As Integer? = Nothing
If score.HasValue Then Console.WriteLine(score.Value)
' varType() returns the Type object
Console.WriteLine(name.GetType().Name) ' String
End Sub
End Module常量、枚举与结构
Const 声明编译时常量(隐式 Shared,不可更改)。Enum 定义命名的整数常量 —— 使用 [Enum].Parse 转换字符串,使用 CInt 获取数值。方括号表示法 [Error] 可转义保留关键字。<Flags> 特性将枚举标记为位域,允许使用 Or/And 运算符组合 —— 常用于权限和选项。始终为标志指定基础类型(As Integer)以控制位宽。
' constants (compile-time, implicitly Shared)
Public Const Pi As Double = 3.14159265358979
Public Const MaxRetries As Integer = 3
' enum: named integer constants
Public Enum LogLevel
Debug = 0
Info = 1
Warning = 2
[Error] = 3 ' brackets for reserved words
End Enum
Module EnumsDemo
Sub Main()
Dim level As LogLevel = LogLevel.Warning
Console.WriteLine(level) ' Warning
Console.WriteLine(CInt(level)) ' 2
' parse string to enum
Dim parsed = [Enum].Parse(GetType(LogLevel), "Info")
Console.WriteLine(parsed) ' Info
' Flags attribute for bit-field enums
Dim perms As FilePermission = FilePermission.Read Or FilePermission.Write
If (perms And FilePermission.Read) <> 0 Then
Console.WriteLine("Has read")
End If
End Sub
End Module
<Flags>
Public Enum FilePermission As Integer
None = 0
Read = 1
Write = 2
Execute = 4
End Enum类型转换与强制转换
Widening 转换(Integer 到 Double)是隐式且安全的。Narrowing 转换(Double 到 Integer)需要显式转换。CInt/CStr/CDate/CDec 是 VB 的转换函数(CType 是通用版本)。DirectCast 最严格 —— 仅在运行时类型完全匹配时有效。TryCast 在失败时返回 Nothing 而非抛出异常(仅限引用类型)。对于用户输入,始终优先使用 TryParse 而非 Parse 以避免异常。IsNumeric/IsDate 是方便的验证辅助方法。CInt 进行舍入(银行家舍入),而 Int() 进行向下取整。
' widening conversions (implicit, safe)
Dim i As Integer = 42
Dim d As Double = i ' Integer -> Double (no data loss)
' narrowing conversions (explicit, may lose data)
Dim d2 As Double = 3.99
Dim i2 As Integer = CInt(d2) ' 4 (rounds, not truncates)
Dim i3 As Integer = Int(d2) ' 3 (floor)
Dim s As String = CStr(42) ' "42"
Dim n As Integer = CInt("100") ' 100 (throws if invalid)
' DirectCast vs CType vs TryCast
Dim obj As Object = "Hello"
Dim s2 As String = DirectCast(obj, String) ' strict, same type only
Dim s3 As String = CType(obj, String) ' flexible, converts
' TryCast: returns Nothing if cast fails (reference types only)
Dim s4 As String = TryCast(obj, String)
If s4 IsNot Nothing Then Console.WriteLine(s4)
' conversion functions
Dim b As Boolean = CBool(1) ' True
Dim dt As Date = CDate("2024-06-18")
Dim dec As Decimal = CDec("99.99")
' IsNumeric, IsDate checks
If IsNumeric("42") Then Console.WriteLine("is number")
If IsDate("2024-06-18") Then Console.WriteLine("is date")
' Parse vs TryParse (safer)
Dim num As Integer
If Integer.TryParse("123", num) Then
Console.WriteLine(num) ' 123
End If运算符与表达式
VB 使用 \ 进行整数除法,使用 / 进行实数除法(不同于 C# 使用 / 和 %)。Mod 是取余运算符。VB 使用 = 同时表示赋值和相等比较(由上下文决定)。And/Or 会计算两个操作数;AndAlso/OrElse 短路计算(性能更佳且可避免空引用)。& 是字符串连接运算符(不是 +,后者可进行数值加法)。默认使用 AndAlso/OrElse 进行短路计算,避免检查 Nothing 对象等错误。
' arithmetic
Dim a As Integer = 10
Console.WriteLine(a + 3) ' 13
Console.WriteLine(a - 4) ' 6
Console.WriteLine(a * 2) ' 20
Console.WriteLine(a / 3) ' 3.333 (Double, real division)
Console.WriteLine(a \ 3) ' 3 (integer division)
Console.WriteLine(a Mod 3) ' 1 (remainder)
Console.WriteLine(a ^ 2) ' 100 (exponent)
' comparison (return Boolean)
Console.WriteLine(5 > 3) ' True
Console.WriteLine(5 = 5) ' True (VB uses = for equality)
Console.WriteLine(5 <> 4) ' True (not equal)
Console.WriteLine("a" < "b") ' True (string comparison)
' logical operators
Dim x As Boolean = True
Dim y As Boolean = False
Console.WriteLine(x And y) ' False (evaluates both)
Console.WriteLine(x Or y) ' True
Console.WriteLine(Not x) ' False
Console.WriteLine(x Xor y) ' True (exclusive or)
Console.WriteLine(x AndAlso y) ' False (short-circuit)
Console.WriteLine(x OrElse y) ' True (short-circuit)
' bitwise (on integers)
Console.WriteLine(5 And 3) ' 1 (0101 And 0011 = 0001)
Console.WriteLine(5 Or 3) ' 7
Console.WriteLine(5 Xor 3) ' 6
' string concatenation
Dim s As String = "Hello" & " " & "World" ' & operator
Dim s2 As String = $"Hello {a}" ' interpolation输入、输出与格式化
Console.WriteLine/Write 是基本输出方法。字符串插值($"...")是嵌入表达式的现代、可读方式 —— 支持格式说明符如 :F2(2 位小数)、:C(货币)、:P(百分比)、:X(十六进制)。String.Format 使用位置占位符 {0}、{1},可选对齐方式({0,-10} = 左对齐,宽度 10)。对于构建大字符串的循环,使用 StringBuilder(而非 &)以避免创建许多中间字符串。Integer.Parse 在无效输入时抛出异常;使用 TryParse 更安全。
Module IO
Sub Main()
' console output
Console.WriteLine("Hello, World!") ' with newline
Console.Write("No newline") ' without newline
Console.WriteLine() ' blank line
' string interpolation ($ before the string)
Dim name As String = "Alice"
Dim age As Integer = 30
Console.WriteLine($"Hello, {name}. You are {age} years old.")
Console.WriteLine($"Next year: {age + 1}") ' expressions work
Console.WriteLine($"Pi: {3.14159:F2}") ' format specifier
' String.Format (positional)
Dim msg As String = String.Format("{0} is {1} years old", name, age)
' composite formatting with alignment
Console.WriteLine("{0,-10} {1,5}", "Alice", 30) ' left/right align
Console.WriteLine("{0:C}", 1234.56) ' currency: $1,234.56
Console.WriteLine("{0:P}", 0.25) ' percent: 25.00 %
Console.WriteLine("{0:X}", 255) ' hex: FF
' console input
Console.Write("Enter name: ")
Dim input As String = Console.ReadLine()
Console.Write("Enter age: ")
Dim ageInput As Integer = Integer.Parse(Console.ReadLine())
' StringBuilder for efficient concatenation
Dim sb As New System.Text.StringBuilder()
For i As Integer = 1 To 100
sb.Append($"Line {i}").AppendLine()
Next
Console.WriteLine(sb.ToString())
End Sub
End Module控制流
If...Then...Else 与三元运算符
If...Then...ElseIf...Else 是 VB 的条件语句。AndAlso/OrElse 短路计算(在不需要时不计算右侧)—— 在条件中始终优先使用它们而非 And/Or。If() 函数(VB 14+)是三元运算符:If(condition, trueVal, falseVal)。带两个参数的 If() 是空合并运算符:If(maybeNull, defaultValue)。避免使用旧的 IIf() 函数 —— 它总是计算两个分支并返回 Object(装箱)。单行 If 不需要 End If。
Dim score As Integer = 85
' multi-line If
If score >= 90 Then
Console.WriteLine("A")
ElseIf score >= 80 Then
Console.WriteLine("B")
ElseIf score >= 70 Then
Console.WriteLine("C")
Else
Console.WriteLine("F")
End If
' single-line If (no End If)
If score > 50 Then Console.WriteLine("Pass")
' nested If with AndAlso (short-circuit)
Dim age As Integer = 25
Dim hasLicense As Boolean = True
If age >= 18 AndAlso hasLicense Then
Console.WriteLine("Can drive")
End If
' If as expression (VB 14+ ternary)
Dim grade As String = If(score >= 60, "Pass", "Fail")
' nullable coalescing
Dim name As String = Nothing
Dim displayName As String = If(name, "Unknown") ' "Unknown"
' IIf function (old-style, always evaluates both sides)
Dim result As String = IIf(score > 50, "Pass", "Fail")Select Case(Switch)
Select Case 是 VB 的 switch 语句 —— 比链式 If...ElseIf 更清晰。Case 支持多个值(逗号分隔)、范围(To)和比较运算符(Is >= 90)。Case Else 是默认分支。与 C# 的 switch 不同,VB 不会 fall-through —— 每个 Case 独立运行,不需要 Break。Select Case 适用于字符串、数字甚至对象(使用 Is)。它非常适合按离散值进行分派。
Dim grade As String = "B"
' basic Select Case
Select Case grade
Case "A"
Console.WriteLine("Excellent")
Case "B", "C" ' multiple values
Console.WriteLine("Good")
Case "D" To "F" ' range
Console.WriteLine("Needs improvement")
Case Else
Console.WriteLine("Invalid grade")
End Select
' numeric ranges
Dim score As Integer = 85
Select Case score
Case Is >= 90 ' comparison operator
Console.WriteLine("A")
Case 80 To 89 ' range
Console.WriteLine("B")
Case 70 To 79
Console.WriteLine("C")
Case Is < 70
Console.WriteLine("F")
End Select
' Case with expressions
Dim day As Integer = 3
Select Case day
Case 1, 2, 3, 4, 5
Console.WriteLine("Weekday")
Case 6, 7
Console.WriteLine("Weekend")
End SelectFor、For Each 与迭代器
For...To...Next 迭代一个范围,可选 Step(负数用于倒计时)。For Each 迭代任何 IEnumerable —— 对集合更清晰。Exit For 跳出循环;Continue For 跳到下一次迭代。迭代器函数(带 Yield)惰性产生序列 —— 值按需生成,对大型或无限序列节省内存。Yield 返回一个值,然后在下一次迭代时从上次离开的位置恢复。这是 VB 对应 C# yield return 的等价物。
' For loop with step
For i As Integer = 0 To 4
Console.WriteLine(i) ' 0 1 2 3 4
Next
For i As Integer = 10 To 1 Step -2
Console.WriteLine(i) ' 10 8 6 4 2
Next
' nested loops
For row As Integer = 1 To 3
For col As Integer = 1 To 3
Console.Write($"{row * col} ")
Next
Console.WriteLine()
Next
' For Each (iterates collections)
Dim fruits() As String = {"apple", "banana", "cherry"}
For Each fruit As String In fruits
Console.WriteLine(fruit)
Next
' For Each with List
Dim nums As New List(Of Integer) From {1, 2, 3}
For Each n As Integer In nums
Console.WriteLine(n)
Next
' Exit For / Continue For
For i As Integer = 1 To 100
If i > 5 Then Exit For ' break
If i Mod 2 = 0 Then Continue For ' skip even
Console.WriteLine(i) ' 1 3 5
Next
' Iterator function (yields one item at a time)
Iterator Function Evens(max As Integer) As IEnumerable(Of Integer)
For i As Integer = 2 To max Step 2
Yield i
Next
End Function
For Each e In Evens(10)
Console.WriteLine(e) ' 2 4 6 8 10
NextDo While、Do Until 与 While
Do While 在条件为 True 时运行;Do Until 在条件为 False 时运行(直到变为 True)。将条件放在底部(Loop While/Until)保证循环体至少运行一次 —— 适用于菜单和输入验证。While...End While 是较旧、更简单的形式。Exit Do 跳出;Continue Do 跳到下一次迭代。当可能不进入循环时选择 Do While;当必须至少运行一次时选择 Do...Loop While。
' Do While: test at top (may never run)
Dim count As Integer = 0
Do While count < 3
Console.WriteLine(count) ' 0 1 2
count += 1
Loop
' Do Until: runs UNTIL condition is true
count = 0
Do Until count >= 3
Console.WriteLine(count) ' 0 1 2
count += 1
Loop
' Do...Loop While: test at bottom (runs at least once)
count = 0
Do
Console.WriteLine(count)
count += 1
Loop While count < 3
' Do...Loop Until: test at bottom, runs until true
count = 0
Do
Console.WriteLine(count)
count += 1
Loop Until count >= 3
' While...End While (older syntax)
Dim i As Integer = 0
While i < 3
Console.WriteLine(i)
i += 1
End While
' Exit Do / Continue Do
Dim n As Integer = 0
Do While n < 100
n += 1
If n Mod 2 = 0 Then Continue Do ' skip even
If n > 10 Then Exit Do ' break
Console.WriteLine(n) ' 1 3 5 7 9
LoopWith...End With 与 GoTo
With...End With 是语法糖,用于访问一个对象的多个成员而无需重复变量名 —— 适用于初始化和构建器。GoTo 跳转到标签;现代 VB 不鼓励使用它,除非用于跳出深度嵌套的循环或在旧的 On Error GoTo 错误处理中。On Error GoTo 是 VB6 风格的错误处理(Resume Next 跳过出错行)—— 在新代码中优先使用结构化 Try/Catch。Err 对象持有最后错误的描述和编号。
' With...End With: access members of one object repeatedly
Dim sb As New System.Text.StringBuilder()
With sb
.Append("Hello")
.Append(", ")
.Append("World")
.AppendLine()
End With
Console.WriteLine(sb.ToString())
' With on anonymous types
Dim person = New With {.Name = "Alice", .Age = 30}
With person
Console.WriteLine($"{.Name} is {.Age}")
End With
' nested With (use caution — confusing)
With New System.Drawing.Point(3, 4)
Console.WriteLine($"X={.X}, Y={.Y}")
End With
' GoTo (use sparingly — only for error handling or deep breaks)
For i As Integer = 1 To 3
For j As Integer = 1 To 3
If i + j > 4 Then GoTo done ' break out of both loops
Console.WriteLine($"{i},{j}")
Next
Next
done:
Console.WriteLine("Finished")
' On Error GoTo (legacy error handling — prefer Try/Catch)
On Error GoTo errorHandler
Dim result As Integer = 10 \ 0
Exit Sub
errorHandler:
Console.WriteLine("Error: " & Err.Description)
Resume Next过程、函数与事件
Sub 与 Function 基础
Sub 执行一个操作(无返回值);Function 返回一个值。Return 退出函数并返回一个值。旧式的"赋值给函数名"(Multiply = a * b)仍然有效,但 Return 更清晰。调用 Sub 时,括号是可选的(Greet "Alice" 或 Greet("Alice"))。为返回值调用的函数需要括号。默认情况下,VB 允许调用函数而不分配返回值,但这可能令人困惑 —— 应明确处理。
' Sub: performs an action, returns nothing
Sub Greet(name As String)
Console.WriteLine($"Hello, {name}!")
End Sub
' Function: returns a value
Function Add(a As Integer, b As Integer) As Integer
Return a + b ' explicit return
End Function
' Function without Return: use function name
Function Multiply(a As Integer, b As Integer) As Integer
Multiply = a * b ' old-style: assign to function name
End Function
' calling
Greet("Alice")
Dim sum As Integer = Add(3, 4)
Console.WriteLine(sum) ' 7
' parentheses are optional for Subs with no return value
Greet "Alice" ' valid (no parens)
' passing arguments by position
Console.WriteLine(Add(1, 2))
' Sub with multiple statements
Sub PrintReport(title As String, lines As Integer)
Console.WriteLine(New String("-"c, 40))
Console.WriteLine(title)
Console.WriteLine(New String("-"c, 40))
For i As Integer = 1 To lines
Console.WriteLine($"Line {i}")
Next
End Sub
PrintReport("Monthly Report", 5)参数:ByVal、ByRef、Optional、ParamArray
ByVal(默认)传递副本 —— 内部更改不影响调用者。ByRef 传递引用 —— 更改确实影响调用者(类似 C# 的 ref)。当过程必须修改调用者的变量或返回多个值时使用 ByRef。可选参数有默认值,必须位于必选参数之后。ParamArray 接受可变数量的参数(类似 C# 的 params),必须是最后一个参数。命名参数(name:=value)提高可读性并允许跳过可选参数。
' ByVal (default): pass by value (copy)
Sub ResetValue(ByVal x As Integer)
x = 0 ' only changes the copy
End Sub
' ByRef: pass by reference (can modify caller's variable)
Sub Increment(ByRef x As Integer)
x += 1 ' modifies the original
End Sub
Dim n As Integer = 5
ResetValue(n)
Console.WriteLine(n) ' 5 (unchanged)
Increment(n)
Console.WriteLine(n) ' 6 (modified)
' Optional parameters (must have default values, must be last)
Function Power(base As Double, Optional exp As Double = 2) As Double
Return Math.Pow(base, exp)
End Function
Console.WriteLine(Power(3)) ' 9 (exp defaults to 2)
Console.WriteLine(Power(2, 10)) ' 1024
' ParamArray: variable number of arguments (must be last)
Function Sum(ParamArray nums() As Integer) As Integer
Dim total As Integer = 0
For Each n In nums
total += n
Next
Return total
End Function
Console.WriteLine(Sum(1, 2, 3)) ' 6
Console.WriteLine(Sum(1, 2, 3, 4, 5)) ' 15
Console.WriteLine(Sum()) ' 0
' named arguments (improve readability)
Sub CreateAccount(name As String, age As Integer, active As Boolean)
' ...
End Sub
CreateAccount(name:="Bob", active:=True, age:=25)Lambda 表达式与委托
Lambda 是内联函数:Function(...) 用于返回值的函数,Sub(...) 用于不返回值的函数。Func(Of T, TResult) 是函数的内置委托类型;Action(Of T) 用于 Sub(无返回值)。AddressOf 从命名方法创建委托。Lambda 对 LINQ 至关重要(Where、Select、OrderBy 接受函数参数)。多行 Lambda 使用 Function...End Function(或 Sub...End Sub)。委托是类型安全的函数指针 —— 适用于回调、事件和策略模式。
' single-line lambda (Function returns a value)
Dim square As Func(Of Integer, Integer) = Function(x) x * x
Console.WriteLine(square(5)) ' 25
' multi-line lambda
Dim factorial As Func(Of Integer, Integer) = Function(n)
Dim result As Integer = 1
For i As Integer = 2 To n
result *= i
Next
Return result
End Function
Console.WriteLine(factorial(5)) ' 120
' Sub lambda (no return value)
Dim log As Action(Of String) = Sub(msg) Console.WriteLine($"[LOG] {msg}")
log("Hello")
' lambda with multiple parameters
Dim add As Func(Of Integer, Integer, Integer) = Function(a, b) a + b
Console.WriteLine(add(3, 4)) ' 7
' use lambdas with LINQ
Dim nums = {1, 2, 3, 4, 5}
Dim evens = nums.Where(Function(n) n Mod 2 = 0).ToList()
Dim doubled = nums.Select(Function(n) n * 2).ToList()
Dim total = nums.Sum(Function(n) n)
' delegate definition
Delegate Function MathOp(a As Integer, b As Integer) As Integer
' assign methods to delegates
Dim op As MathOp
op = AddressOf AddNumbers ' AddNumbers is a regular Function
Console.WriteLine(op(3, 4)) ' 7
' Action and Func (built-in delegate types)
Dim print As Action(Of String) = AddressOf Console.WriteLine
print("Hello")事件与事件处理器
事件启用观察者模式:类引发事件,订阅者处理它们。使用 Event 声明,使用 RaiseEvent 引发,使用 Handles(WithEvents,编译时)或 AddHandler(运行时,动态)处理。WithEvents + Handles 是声明式的,但变量必须是模块/类级别。AddHandler/RemoveHandler 允许在运行时订阅/取消订阅 —— 适用于动态连接。事件处理器必须匹配事件的签名。事件是 Windows Forms 和 WPF 的支柱(按钮点击、窗体加载等)。
' define an event
Public Class TemperatureSensor
Public Event TemperatureChanged(temp As Double)
Private _current As Double
Public Property Current As Double
Get
Return _current
End Get
Set(value As Double)
If value <> _current Then
_current = value
RaiseEvent TemperatureChanged(value) ' fire the event
End If
End Set
End Property
End Class
' subscribe to an event
Module SensorDemo
WithEvents sensor As New TemperatureSensor()
Sub Main()
sensor.Current = 20.5 ' triggers handler
sensor.Current = 21.0 ' triggers handler
End Sub
' event handler (must match the event signature)
Sub OnTempChanged(temp As Double) Handles sensor.TemperatureChanged
Console.WriteLine($"Temperature changed to {temp:F1}°C")
End Sub
End Module
' alternative: AddHandler (dynamic, runtime)
Module DynamicDemo
Sub Main()
Dim s As New TemperatureSensor()
AddHandler s.TemperatureChanged, AddressOf HandleTemp
s.Current = 25.0
RemoveHandler s.TemperatureChanged, AddressOf HandleTemp
End Sub
Sub HandleTemp(temp As Double)
Console.WriteLine($"Got: {temp}")
End Sub
End Module重载、递归与 Shared 成员
重载允许多个方法共享一个名称但参数列表不同 —— 编译器选择最佳匹配。在跨继承重载时需要 Overloads 关键字。递归是函数调用自身 —— 需要基本情况来终止。注意深度递归的栈溢出。Shared(C# 的 static)成员属于类型而非实例 —— 通过 ClassName.Member 调用,无需创建对象。Shared 构造函数每个类型运行一次,非常适合初始化静态数据。
' overloading: same name, different parameters
Class Calculator
Overloads Function Add(a As Integer, b As Integer) As Integer
Return a + b
End Function
Overloads Function Add(a As Double, b As Double) As Double
Return a + b
End Function
Overloads Function Add(values() As Integer) As Integer
Dim total As Integer = 0
For Each v In values
total += v
Next
Return total
End Function
End Class
' recursion: function calls itself
Function Factorial(n As Integer) As Long
If n <= 1 Then Return 1
Return n * Factorial(n - 1)
End Function
Console.WriteLine(Factorial(5)) ' 120
' Fibonacci (inefficient — for demo)
Function Fib(n As Integer) As Integer
If n < 2 Then Return n
Return Fib(n - 1) + Fib(n - 2)
End Function
' Shared (static) members: belong to the class, not instances
Class MathHelper
Public Shared ReadOnly Pi As Double = 3.14159265358979
Public Shared Function CircleArea(radius As Double) As Double
Return Pi * radius * radius
End Function
End Class
' call without creating an instance
Console.WriteLine(MathHelper.Pi)
Console.WriteLine(MathHelper.CircleArea(5))
' Shared constructor (runs once, before any access)
Class Config
Shared Sub New()
Console.WriteLine("Config initialized")
End Sub
End Class字符串与文本处理
字符串基础与不可变性
VB.NET 中的字符串是不可变的 —— 每个方法(ToUpper、Replace 等)都返回一个新字符串;原始字符串不变。使用 = 进行相等比较(VB 特有)。IndexOf 返回子字符串的第一个索引(未找到返回 -1)。Split 将字符串拆分为数组;Join 将数组合并为字符串。Trim 移除两端的空白字符(或指定字符)。对于不区分大小写的比较,使用 StringComparison.OrdinalIgnoreCase。对于循环中的大量字符串操作,使用 StringBuilder。
Dim s As String = "Hello, World"
' properties
Console.WriteLine(s.Length) ' 12
Console.WriteLine(s(0)) ' H (indexer, read-only)
' comparison
Console.WriteLine("abc" = "abc") ' True (= is equality in VB)
Console.WriteLine("abc".Equals("abc")) ' True
Console.WriteLine(String.Equals("a", "A", StringComparison.OrdinalIgnoreCase)) ' True
' case conversion (returns new string — strings are immutable)
Console.WriteLine(s.ToUpper()) ' HELLO, WORLD
Console.WriteLine(s.ToLower()) ' hello, world
' searching
Console.WriteLine(s.IndexOf("o")) ' 4 (first occurrence)
Console.WriteLine(s.LastIndexOf("o")) ' 8
Console.WriteLine(s.Contains("World")) ' True
Console.WriteLine(s.StartsWith("Hello")) ' True
Console.WriteLine(s.EndsWith("World")) ' True
' substring
Console.WriteLine(s.Substring(7)) ' World
Console.WriteLine(s.Substring(0, 5)) ' Hello
' split and join
Dim parts() As String = "a,b,c,d".Split(","c)
Console.WriteLine(String.Join("-", parts)) ' a-b-c-d
' replace and trim
Console.WriteLine(s.Replace("o", "0")) ' Hell0, W0rld
Console.WriteLine(" hi ".Trim()) ' hi
Console.WriteLine(" hi ".TrimStart()) ' hi<spaces>
Console.WriteLine("xxhelloxx".Trim("x"c)) ' helloStringBuilder 与性能
String 是不可变的 —— 每个 & 或 += 都会创建一个新字符串,复制所有先前内容。在循环中,这是 O(n²)。StringBuilder 是可变的 —— Append/Insert/Remove 修改同一缓冲区,使其为 O(n)。对于构建大字符串的循环,始终使用 StringBuilder。StringBuilder 可链式调用(Append 返回同一实例)。如果知道大致大小,设置初始 Capacity 以避免重新分配。对于一次性连接(不在循环中),& 没问题且更可读。
Imports System.Text
' StringBuilder: mutable string for efficient concatenation
Dim sb As New StringBuilder()
For i As Integer = 1 To 1000
sb.Append("Line ").Append(i).AppendLine() ' chainable
Next
Dim result As String = sb.ToString()
' StringBuilder vs & concatenation
' BAD: creates 1000 intermediate strings
Dim bad As String = ""
For i As Integer = 1 To 1000
bad &= $"Line {i}" ' creates new string each time
Next
' GOOD: StringBuilder mutates in place
Dim good As New StringBuilder()
For i As Integer = 1 To 1000
good.AppendLine($"Line {i}")
Next
' useful StringBuilder methods
Dim sb2 As New StringBuilder("Hello")
sb2.Append(", World") ' append string
sb2.AppendLine() ' append newline
sb2.AppendFormat("Count: {0}", 42) ' formatted append
sb2.Insert(0, ">> ") ' insert at position
sb2.Remove(0, 3) ' remove 3 chars at position 0
sb2.Replace("World", "VB") ' replace substring
Console.WriteLine(sb2.ToString())
Console.WriteLine(sb2.Length)
' pre-allocate capacity for known sizes
Dim sb3 As New StringBuilder(10000) ' initial capacity字符串插值与格式化
字符串插值($"...")是嵌入变量和表达式的现代方式 —— 可读且支持冒号后的格式说明符(:C 货币、:F2 固定小数、:N 千位分隔符、:X 十六进制、:D5 零填充、:P 百分比)。日期格式:yyyy-MM-dd、HH:mm:ss、dddd(完整星期名)。使用 String.Format 进行位置占位符或动态构建格式字符串时。对于文化感知应用(国际化),向 ToString/Format 传递 CultureInfo 以控制小数分隔符、货币符号和日期格式。
Dim name As String = "Alice"
Dim age As Integer = 30
Dim salary As Decimal = 50000.5D
Dim today As Date = Date.Now
' interpolated string ($ prefix)
Console.WriteLine($"Name: {name}, Age: {age}")
Console.WriteLine($"Next year: {age + 1}") ' expressions
Console.WriteLine($"Upper: {name.ToUpper()}") ' method calls
' format specifiers in interpolation
Console.WriteLine($"Salary: {salary:C}") ' $50,000.50
Console.WriteLine($"Salary: {salary:N2}") ' 50,000.50
Console.WriteLine($"Pi: {Math.PI:F4}") ' 3.1416
Console.WriteLine($"Date: {today:yyyy-MM-dd}") ' 2024-06-18
Console.WriteLine($"Time: {today:HH:mm:ss}") ' 14:30:00
Console.WriteLine($"Hex: {255:X}") ' FF
Console.WriteLine($"Padded: {42:D5}") ' 00042
Console.WriteLine($"Percent: {0.25:P0}") ' 25%
' alignment in interpolation
Console.WriteLine($"{"Name",-10} | {"Age",5}") ' left/right align
Console.WriteLine($"{name,-10} | {age,5}")
' String.Format (positional, older style)
Dim msg As String = String.Format("{0} is {1} years old", name, age)
' composite formatting with format string
Console.WriteLine(String.Format("Price: {0:C2}", 19.99))
' custom date formats
Console.WriteLine(today.ToString("dddd, MMMM d, yyyy"))
Console.WriteLine(today.ToString("yyyy/MM/dd HH:mm"))
' culture-specific formatting
Dim ci As Globalization.CultureInfo = New Globalization.CultureInfo("fr-FR")
Console.WriteLine(salary.ToString("C", ci)) ' 50 000,50 €正则表达式
Regex.IsMatch 测试是否匹配;Regex.Matches 查找所有匹配;Regex.Match 查找第一个。组用括号捕获匹配的部分 —— 通过 Groups(1)、Groups(2) 访问。Regex.Replace 替换匹配(使用 $1、$2 引用组)。RegexOptions.Compiled 编译模式以加快重复匹配。常见模式:\d(数字)、\w(单词字符)、\s(空白)、+(一个或多个)、*(零个或多个)、{n}(恰好 n 个)、^/$(开始/结束)。始终用正则表达式验证用户输入的电子邮件、电话等。
Imports System.Text.RegularExpressions
' basic matching
Dim pattern As String = "d+" ' one or more digits
Dim input As String = "abc 123 def 456"
If Regex.IsMatch(input, pattern) Then
Console.WriteLine("Contains digits")
End If
' extract matches
For Each m As Match In Regex.Matches(input, "d+")
Console.WriteLine(m.Value) ' 123, 456
Next
' single match with groups
Dim m2 As Match = Regex.Match("2024-06-18", "(d{4})-(d{2})-(d{2})")
If m2.Success Then
Console.WriteLine(m2.Groups(1).Value) ' 2024 (year)
Console.WriteLine(m2.Groups(2).Value) ' 06 (month)
Console.WriteLine(m2.Groups(3).Value) ' 18 (day)
End If
' replace
Dim cleaned As String = Regex.Replace("phone: 123-456-7890", "D", "")
Console.WriteLine(cleaned) ' 1234567890 (digits only)
' replace with match reference ($1, $2)
Dim swapped As String = Regex.Replace("John Doe", "(w+) (w+)", "$2, $1")
Console.WriteLine(swapped) ' Doe, John
' split
Dim parts() As String = Regex.Split("a,b;;c", "[,;]+")
' common patterns
Dim emailPattern As String = "^[w.-]+@[w.-]+.w+$"
Dim urlPattern As String = "^https?://[w./-]+$"
Dim phonePattern As String = "^d{3}-d{3}-d{4}$"
' compiled regex (faster for repeated use)
Dim rx As New Regex("d+", RegexOptions.Compiled)
Dim count As Integer = rx.Matches(input).Count字符操作与编码
Char 是单个 Unicode 字符(2 字节)。使用 c 后缀表示字符字面量("A"c)。Char 方法(IsDigit、IsLetter、IsUpper)适用于验证。Asc/Chr 在字符和 ASCII 码之间转换。ToCharArray 将字符串转换为可变的字符数组(字符串本身不可变)。Encoding.UTF8.GetBytes 将字符串转换为字节数组(对文件 I/O 和网络必不可少)。Base64(Convert.ToBase64String)将二进制数据编码为文本 —— 用于数据 URI、电子邮件附件和 API。
' Char basics
Dim c As Char = "A"c ' char literal (c suffix)
Console.WriteLine(Char.IsDigit("5"c)) ' True
Console.WriteLine(Char.IsLetter("A"c)) ' True
Console.WriteLine(Char.IsWhiteSpace(" "c)) ' True
Console.WriteLine(Char.ToUpper("a"c)) ' A
Console.WriteLine(Char.ToLower("A"c)) ' a
' convert between char and ASCII code
Dim code As Integer = Asc("A"c) ' 65
Dim ch As Char = Chr(66) ' B
' iterate characters in a string
Dim s As String = "Hello"
For Each c In s
Console.Write($"{Asc(c)} ") ' 72 101 108 108 111
Next
' char array <-> string
Dim chars() As Char = s.ToCharArray()
Array.Reverse(chars)
Dim reversed As String = New String(chars)
Console.WriteLine(reversed) ' olleH
' encoding: string <-> bytes
Dim text As String = "Hello"
Dim bytes() As Byte = System.Text.Encoding.UTF8.GetBytes(text)
Console.WriteLine(bytes.Length) ' 5
Dim decoded As String = System.Text.Encoding.UTF8.GetString(bytes)
' ASCII encoding (1 byte per char, no Unicode)
Dim asciiBytes() As Byte = System.Text.Encoding.ASCII.GetBytes(text)
' Base64 encoding (for binary data in text)
Dim b64 As String = Convert.ToBase64String(bytes)
Dim original() As Byte = Convert.FromBase64String(b64)
' StringBuilder from char array
Dim sb As New System.Text.StringBuilder(New String(chars))数组与集合
数组:声明、索引与方法
VB 中的数组从 0 开始索引且大小固定。Dim arr(n) 创建 n+1 个元素(0 到 n)。数组字面量使用 { }。多维数组 (,) 是矩形的;锯齿数组 ()() 是数组的数组(每行可以有不同的长度)。ReDim 调整数组大小 —— Preserve 保留现有值(没有它,值会被清除)。Array.Sort/Reverse/Clear/IndexOf/Copy 是静态辅助方法。当大小频繁变化时,使用 List(Of T) 代替数组。
' fixed-size array
Dim nums(4) As Integer ' 5 elements (0-4), default 0
nums(0) = 10
nums(1) = 20
' array literal
Dim fruits() As String = {"apple", "banana", "cherry"}
Dim nums2() As Integer = {1, 2, 3, 4, 5}
' type inference
Dim mixed = {1, 2, 3} ' Integer()
Dim names = {"Alice", "Bob"} ' String()
' indexing (0-based)
Console.WriteLine(fruits(0)) ' apple
Console.WriteLine(fruits.Length) ' 3
Console.WriteLine(nums2.GetUpperBound(0)) ' 4 (last index)
' multi-dimensional arrays
Dim grid(2, 2) As Integer ' 3x3
grid(0, 0) = 1
grid(1, 2) = 5
' jagged arrays (array of arrays)
Dim jagged()() As Integer = {
New Integer() {1, 2},
New Integer() {3, 4, 5},
New Integer() {6}
}
Console.WriteLine(jagged(1)(2)) ' 5
' Array class methods
Array.Sort(nums2) ' sort in place
Array.Reverse(nums2) ' reverse
Array.Clear(nums2, 0, 2) ' set first 2 to 0
Dim found As Integer = Array.IndexOf(nums2, 3) ' search
Dim copy(4) As Integer
Array.Copy(nums2, copy, 5) ' copy
' resize (creates new array)
ReDim Preserve nums2(9) ' resize, keep existing values
' ReDim without Preserve clears values
' iterate
For Each n As Integer In nums2
Console.WriteLine(n)
NextList(Of T):动态数组
List(Of T) 是首选的动态集合 —— 它在 Add 时自动增长。Count 给出元素数量(而非容量)。Add/Insert/Remove/RemoveAt/Contains/IndexOf 是核心方法。Find/FindAll/Exists 接受谓词(lambda)进行自定义搜索。Sort 可以接受 Comparison 委托进行自定义排序。List(Of T) 内部包装一个数组并在满时调整大小(摊销 O(1) Add)。当需要固定大小集合时,使用 ToArray() 转换为数组。
Imports System.Collections.Generic
' create and initialize
Dim nums As New List(Of Integer) From {1, 2, 3}
Dim names As New List(Of String)
' add and insert
nums.Add(4) ' add to end
nums.AddRange({5, 6, 7}) ' add multiple
nums.Insert(0, 0) ' insert at index
Console.WriteLine(nums.Count) ' 8
' access and modify
Console.WriteLine(nums(0)) ' 0 (indexer)
nums(0) = 100 ' modify
' remove
nums.Remove(3) ' remove by value (first match)
nums.RemoveAt(0) ' remove by index
nums.RemoveRange(0, 2) ' remove 2 starting at index 0
nums.Clear() ' remove all
' search
nums = New List(Of Integer) From {1, 2, 3, 4, 5}
Console.WriteLine(nums.Contains(3)) ' True
Console.WriteLine(nums.IndexOf(3)) ' 2
Dim found As Boolean = nums.Exists(Function(n) n > 3)
Dim first As Integer = nums.Find(Function(n) n > 3) ' 4
Dim all As List(Of Integer) = nums.FindAll(Function(n) n > 2) ' 3,4,5
' iterate
For Each n As Integer In nums
Console.WriteLine(n)
Next
' convert to array
Dim arr() As Integer = nums.ToArray()
' sort and reverse
nums.Sort()
nums.Reverse()
' List with custom objects
Dim people As New List(Of (Name As String, Age As Integer))
people.Add(("Alice", 30))
people.Add(("Bob", 25))
people.Sort(Function(a, b) a.Age.CompareTo(b.Age)) ' sort by ageDictionary、HashSet 与 Queue/Stack
Dictionary(Of TKey, TValue) 将键映射到值 —— 按键 O(1) 查找。使用 TryGetValue 避免异常和双重查找(ContainsKey + 索引器)。使用 KeyValuePair 迭代。HashSet(Of T) 存储唯一元素,O(1) 添加/包含 —— 用于去重和集合操作(UnionWith、IntersectWith、ExceptWith)。Queue 是 FIFO(Enqueue/Dequeue);Stack 是 LIFO(Push/Pop)。所有这些集合都是泛型的(类型安全,无装箱)。根据访问模式选择:查找 → Dictionary,唯一性 → HashSet,顺序 → Queue/Stack。
Imports System.Collections.Generic
' Dictionary: key-value pairs
Dim ages As New Dictionary(Of String, Integer) From {
{"Alice", 30},
{"Bob", 25}
}
ages("Eve") = 28 ' add or update
ages.Add("Carol", 22) ' add (throws if key exists)
' access
Console.WriteLine(ages("Alice")) ' 30
Console.WriteLine(ages.ContainsKey("Bob")) ' True
Console.WriteLine(ages.ContainsValue(28)) ' True
' safe access (TryGetValue avoids double lookup)
Dim age As Integer
If ages.TryGetValue("Alice", age) Then
Console.WriteLine(age) ' 30
End If
' iterate
For Each kv As KeyValuePair(Of String, Integer) In ages
Console.WriteLine($"{kv.Key}: {kv.Value}")
Next
' remove
ages.Remove("Bob")
' HashSet: unique elements
Dim unique As New HashSet(Of Integer) From {1, 2, 3}
unique.Add(2) ' no effect (already exists)
unique.Add(4)
Console.WriteLine(unique.Count) ' 4
' set operations
Dim setA As New HashSet(Of Integer) From {1, 2, 3}
Dim setB As New HashSet(Of Integer) From {3, 4, 5}
setA.UnionWith(setB) ' {1,2,3,4,5}
setA.IntersectWith(setB) ' {3}
setA.ExceptWith(setB) ' remove setB from setA
' Queue: FIFO
Dim q As New Queue(Of String)
q.Enqueue("first")
q.Enqueue("second")
Console.WriteLine(q.Dequeue()) ' first
Console.WriteLine(q.Peek()) ' second (without removing)
' Stack: LIFO
Dim stk As New Stack(Of Integer)
stk.Push(1)
stk.Push(2)
Console.WriteLine(stk.Pop()) ' 2
Console.WriteLine(stk.Peek()) ' 1LINQ:查询与方法语法
LINQ(Language Integrated Query)以声明式方式转换集合。查询语法(From...Where...Select)类似 SQL,对复杂查询可读性好。方法语法(.Where().Select())是流式的且链式良好。两者编译为相同的代码。关键运算符:Where(过滤)、Select(转换)、OrderBy(排序)、GroupBy(分组)、Take/Skip(分页)、First/FirstOrDefault(查找)、Any/All(测试)、Sum/Average/Max/Min(聚合)。FirstOrDefault 在无匹配时返回默认值(0、Nothing)—— 避免异常。LINQ 适用于任何 IEnumerable(数组、列表、字典)。
Imports System.Linq
Dim nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
Dim people = {
New With {.Name = "Alice", .Age = 30, .City = "NYC"},
New With {.Name = "Bob", .Age = 25, .City = "LA"},
New With {.Name = "Carol", .Age = 35, .City = "NYC"}
}
' query syntax (SQL-like)
Dim adults = From p In people
Where p.Age >= 30
Select p.Name
' Alice, Carol
Dim nycNames = From p In people
Where p.City = "NYC"
Order By p.Age
Select p.Name, p.Age
' method syntax (fluent, with lambdas)
Dim evens = nums.Where(Function(n) n Mod 2 = 0).ToList() ' 2,4,6,8,10
Dim doubled = nums.Select(Function(n) n * 2).ToList()
Dim sum = nums.Sum() ' 55
Dim avg = nums.Average() ' 5.5
Dim max = nums.Max()
Dim count = nums.Count(Function(n) n > 5) ' 5
' ordering
Dim sorted = nums.OrderBy(Function(n) n).ToList()
Dim descSorted = nums.OrderByDescending(Function(n) n).ToList()
' grouping
Dim byCity = people.GroupBy(Function(p) p.City)
For Each g In byCity
Console.WriteLine($"{g.Key}: {g.Count()} people")
Next
' take / skip (pagination)
Dim first3 = nums.Take(3).ToList() ' 1,2,3
Dim skip3 = nums.Skip(3).Take(3).ToList() ' 4,5,6
' first / single / elementAt
Dim first = nums.First() ' 1
Dim firstEven = nums.First(Function(n) n Mod 2 = 0) ' 2
Dim maybeFirst = nums.FirstOrDefault(Function(n) n > 100) ' 0 (default)
' any / all
Dim hasEven = nums.Any(Function(n) n Mod 2 = 0) ' True
Dim allPositive = nums.All(Function(n) n > 0) ' True
' aggregate
Dim product = nums.Aggregate(Function(acc, n) acc * n) ' factorial-like集合初始化与元组
集合初始化器(From { })在一条语句中创建并填充集合。元组(VB 2017+)将多个值分组 —— 命名字段(X、Y)比 Item1、Item2 更清晰。元组非常适合从函数返回多个值而无需定义类。解构(Dim (a, b) = tuple)将元组拆分为变量。ToDictionary 将列表转换为字典(键选择器 + 值选择器)。元组是值类型(结构),因此对于小型临时分组很高效。
Imports System.Collections.Generic
' collection initializers (From clause)
Dim nums As New List(Of Integer) From {1, 2, 3, 4, 5}
Dim dict As New Dictionary(Of String, Integer) From {
{"one", 1},
{"two", 2},
{"three", 3}
}
Dim set_ As New HashSet(Of String) From {"a", "b", "c"}
' tuples (VB 2017+)
Dim point As (X As Integer, Y As Integer) = (X := 3, Y := 4)
Console.WriteLine(point.X) ' 3
Console.WriteLine(point.Y) ' 4
' tuple without named fields (Item1, Item2, ...)
Dim t As (String, Integer) = ("Alice", 30)
Console.WriteLine(t.Item1) ' Alice
Console.WriteLine(t.Item2) ' 30
' tuple as function return (multiple values)
Function GetStats(nums As List(Of Integer)) As (Count As Integer, Sum As Integer, Avg As Double)
Return (
nums.Count,
nums.Sum(),
nums.Average()
)
End Function
Dim stats = GetStats(nums)
Console.WriteLine($"Count={stats.Count}, Sum={stats.Sum}, Avg={stats.Avg:F2}")
' deconstruct tuples
Dim (name, age) = ("Bob", 25)
Console.WriteLine($"{name}, {age}")
' list of tuples
Dim people As New List(Of (Name As String, Age As Integer)) From {
("Alice", 30),
("Bob", 25)
}
' iterate with index
For i As Integer = 0 To nums.Count - 1
Console.WriteLine($"{i}: {nums(i)}")
Next
' LINQ to dictionary
Dim nameToAge = people.ToDictionary(Function(p) p.Name, Function(p) p.Age)
Console.WriteLine(nameToAge("Alice")) ' 30面向对象编程
类、字段与属性
类将数据(字段/属性)和行为(方法)捆绑在一起。自动实现属性(Property X As Type)自动生成隐藏的支持字段 —— 对简单数据很简洁。完整属性(Get/Set 块)允许验证、计算或副作用。ReadOnly 属性只有 Get。Me 指向当前实例(类似 C# 的 this)。构造函数(Sub New)初始化对象。属性对调用者看起来像字段(p.Name)但执行代码 —— 这种封装是 OOP 的核心优势。
Public Class Person
' private field (backing store)
Private _name As String
Private _age As Integer
' auto-implemented property (compiler creates the field)
Public Property Email As String
' property with custom logic
Public Property Name As String
Get
Return _name
End Get
Set(value As String)
If String.IsNullOrEmpty(value) Then
Throw New ArgumentException("Name required")
End If
_name = value
End Set
End Property
' property with validation
Public Property Age As Integer
Get
Return _age
End Get
Set(value As Integer)
If value < 0 OrElse value > 150 Then
Throw New ArgumentOutOfRangeException("Age")
End If
_age = value
End Set
End Property
' read-only property (computed)
Public ReadOnly Property IsAdult As Boolean
Get
Return _age >= 18
End Get
End Property
' constructor
Public Sub New(name As String, age As Integer)
Me.Name = name ' Me refers to the current instance
Me.Age = age
End Sub
End Class
' usage
Dim p As New Person("Alice", 30)
Console.WriteLine(p.Name) ' Alice
Console.WriteLine(p.IsAdult) ' True
p.Age = 31 ' uses setter
' p.Age = 200 ' throws (validation)继承与 MyBase
Inherits 建立 IS-A 关系(Dog IS an Animal)。MyBase.New() 调用基类构造函数(必须是第一条语句)。Overridable(基类)+ Overrides(派生类)启用多态 —— 即使通过基类引用访问,派生方法也会运行。Shadows(或 Overloads)隐藏基类成员而非重写(无多态)。使用 Overridable/Overrides 实现真正的多态;仅在必须隐藏无法更改的成员时使用 Shadows。VB 支持单继承(一个基类)但支持多接口。
' base class
Public Class Animal
Public Property Name As String
Public Sub New(name As String)
Me.Name = name
End Sub
Public Overridable Function Speak() As String
Return Name & " makes a sound"
End Function
Public Overridable Sub Eat()
Console.WriteLine(Name & " is eating")
End Sub
End Class
' derived class (Inherits)
Public Class Dog
Inherits Animal
Public Property Breed As String
' call base constructor with MyBase
Public Sub New(name As String, breed As String)
MyBase.New(name) ' must be first line
Me.Breed = breed
End Sub
' override virtual method
Public Overrides Function Speak() As String
Return Name & " says Woof"
End Function
' extend base method
Public Overrides Sub Eat()
MyBase.Eat() ' call base implementation
Console.WriteLine(Name & " wags tail")
End Sub
' new method (not override — hides base)
Public Shadows Sub Description()
Console.WriteLine($"{Name} ({Breed})")
End Sub
End Class
Dim d As New Dog("Rex", "Labrador")
Console.WriteLine(d.Speak()) ' Rex says Woof
d.Eat() ' Rex is eating / Rex wags tail
' polymorphism: base reference, derived object
Dim a As Animal = New Dog("Buddy", "Poodle")
Console.WriteLine(a.Speak()) ' Buddy says Woof (virtual dispatch)接口与多态
接口定义契约(方法、属性)而无实现 —— 类实现它们。与继承不同,一个类可以实现多个接口。Implements 关键字将成员链接到其接口声明(VB 特有语法)。接口启用多态:代码可以与任何 IDrawable 一起工作,而无需知道它是 Circle 还是 Square。TypeOf x Is T 检查运行时类型;DirectCast 进行转换(无效时抛出异常)。使用接口解耦代码:依赖 IDrawable,而非 Circle。这是依赖倒置原则。
' interface: contract (no implementation)
Public Interface IComparable(Of T)
Function CompareTo(other As T) As Integer
End Interface
Public Interface IDrawable
Sub Draw()
Property X As Integer
Property Y As Integer
End Interface
' implement an interface
Public Class Circle
Implements IDrawable
Public Property X As Integer Implements IDrawable.X
Public Property Y As Integer Implements IDrawable.Y
Public Property Radius As Integer
Public Sub Draw() Implements IDrawable.Draw
Console.WriteLine($"Drawing circle at ({X}, {Y}) r={Radius}")
End Sub
End Class
Public Class Square
Implements IDrawable
Public Property X As Integer Implements IDrawable.X
Public Property Y As Integer Implements IDrawable.Y
Public Property Side As Integer
Public Sub Draw() Implements IDrawable.Draw
Console.WriteLine($"Drawing square at ({X}, {Y}) side={Side}")
End Sub
End Class
' polymorphism via interface
Dim shapes As New List(Of IDrawable) From {
New Circle With {.X = 0, .Y = 0, .Radius = 5},
New Square With {.X = 10, .Y = 10, .Side = 4}
}
For Each s In shapes
s.Draw() ' calls the right implementation
Next
' check and cast
Dim shape As IDrawable = New Circle()
If TypeOf shape Is Circle Then
Dim c As Circle = DirectCast(shape, Circle)
c.Radius = 10
End If
' multiple interfaces
Public Class TextBox
Implements IDrawable, IComparable(Of TextBox)
' ... implement both
End Class泛型与约束
泛型(Of T)让你编写类型安全、可重用的代码,适用于任何类型 —— 无装箱、无转换、编译时类型检查。List(Of T)、Dictionary(Of K,V)、Stack(Of T) 是泛型集合。约束限制类型参数:Class(引用类型)、Structure(值类型)、New(有无参构造函数 —— 允许 New T())、特定基类或接口。泛型避免了装箱(值类型)的性能损失和 Object 转换的脆弱性。始终优先使用泛型集合(List(Of T))而非非泛型(ArrayList)。
' generic class
Public Class Stack(Of T)
Private items As New List(Of T)
Public Sub Push(item As T)
items.Add(item)
End Sub
Public Function Pop() As T
If items.Count = 0 Then Throw New InvalidOperationException("Empty")
Dim last As T = items(items.Count - 1)
items.RemoveAt(items.Count - 1)
Return last
End Function
Public ReadOnly Property Count As Integer
Get
Return items.Count
End Get
End Property
End Class
' use with different types
Dim intStack As New Stack(Of Integer)
intStack.Push(1)
intStack.Push(2)
Console.WriteLine(intStack.Pop()) ' 2
Dim strStack As New Stack(Of String)
strStack.Push("Hello")
Console.WriteLine(strStack.Pop()) ' Hello
' generic method with constraint
Public Class Repository(Of T As {Class, New})
' T must be a reference type (Class) and have a parameterless constructor (New)
Private items As New List(Of T)
Public Sub Add(item As T)
items.Add(item)
End Sub
Public Function Find(match As Predicate(Of T)) As T
Return items.Find(match)
End Function
End Class
' generic function
Function FirstOrDefault(Of T)(list As IEnumerable(Of T), predicate As Func(Of T, Boolean)) As T
For Each item In list
If predicate(item) Then Return item
Next
Return Nothing ' default for reference types
End Function
' constraints:
' Class - reference type
' Structure - value type
' New - has parameterless constructor
' BaseClass - inherits from a specific class
' Interface - implements an interface分部类与命名空间
分部类将一个类拆分到多个文件中 —— 编译器合并它们。适用于将生成的代码(设计器文件)与手写代码分离,或拆分大型类。命名空间组织类型并防止名称冲突(MyApp.Models.User vs MyApp.Services.User)。Imports 将命名空间名称引入作用域(无需完全限定)。RootNamespace(项目设置)包装所有类型 —— 将其设置为你的公司/产品名称。约定:命名空间匹配文件夹结构;每个逻辑区域一个命名空间。
' File1.vb
Namespace MyApp.Models
Partial Public Class User
Public Property Id As Integer
Public Property Name As String
Public Property Email As String
End Class
End Namespace
' File2.vb (same class, different file)
Namespace MyApp.Models
Partial Public Class User
Public Sub SendWelcomeEmail()
Console.WriteLine($"Welcome {Name}!")
End Sub
Public Function Validate() As Boolean
Return Not String.IsNullOrEmpty(Email)
End Function
End Class
End Namespace
' usage (combines both files)
Dim u As New MyApp.Models.User With {
.Id = 1,
.Name = "Alice",
.Email = "[email protected]"
}
u.SendWelcomeEmail()
Console.WriteLine(u.Validate()) ' True
' namespace nesting
Namespace MyApp.Services
Public Class UserService
Public Sub Create(name As String)
' ...
End Sub
End Class
End Namespace
' import to use short names
Imports MyApp.Models
Imports MyApp.Services
Module Program
Sub Main()
Dim user As New User() ' no full namespace needed
Dim svc As New UserService()
End Sub
End Module错误处理与异常
Try...Catch...Finally
Try/Catch/Finally 是结构化异常处理。Catch 块按顺序检查 —— 先放特定异常,Exception(基类)最后。Finally 总是运行(即使在 Return 或未捕获异常时)—— 用于清理(关闭文件、释放资源)。Throw(裸)重新引发当前异常,保留堆栈跟踪。When 过滤器有条件地捕获(Catch...When condition)。Exception 属性:Message(描述)、StackTrace(调用链)、Source(程序集)。切勿在不记录的情况下静默捕获 Exception —— 这会隐藏 bug。
Try
Dim x As Integer = 10
Dim y As Integer = 0
Dim z As Integer = x \ y ' integer division by zero
Catch ex As DivideByZeroException
Console.WriteLine("Cannot divide by zero: " & ex.Message)
Catch ex As OverflowException
Console.WriteLine("Number too large: " & ex.Message)
Catch ex As Exception ' catch-all (must be last)
Console.WriteLine("Unexpected error: " & ex.Message)
Finally
' always runs (even with Return or exception)
Console.WriteLine("Cleanup complete")
End Try
' nested Try
Try
Try
Dim arr() As Integer = {1, 2, 3}
Console.WriteLine(arr(10)) ' IndexOutOfRangeException
Catch ex As IndexOutOfRangeException
Console.WriteLine("Index error: " & ex.Message)
Throw ' re-throw to outer handler
End Try
Catch ex As Exception
Console.WriteLine("Outer caught: " & ex.Message)
End Try
' When filter (conditional catch)
Try
' risky code
Catch ex As Exception When ex.Message.Contains("network")
Console.WriteLine("Network error, retrying...")
End Try
' exception properties
Try
Dim n As Integer = Integer.Parse("abc")
Catch ex As FormatException
Console.WriteLine(ex.Message) ' human-readable
Console.WriteLine(ex.GetType().Name) ' FormatException
Console.WriteLine(ex.StackTrace) ' where it happened
Console.WriteLine(ex.Source) ' which assembly
End Try抛出与自定义异常
Throw New ExceptionType(...) 引发异常。始终传递有意义的消息,并使用 NameOf() 表示参数名(重构安全)。包装异常时,将原始异常作为 innerException 传递以保留原因。自定义异常应继承自 Exception(或更具体的基类),标记 <Serializable>,并提供构造函数(message、message+inner)。重写 ToString() 进行自定义显示。自定义异常让调用者捕获特定错误类型并适当处理 —— 用于领域特定错误(InvalidTransactionException、PaymentFailedException)。
' throw a built-in exception
Sub CheckAge(age As Integer)
If age < 0 Then
Throw New ArgumentOutOfRangeException(NameOf(age), "Age cannot be negative")
ElseIf age > 150 Then
Throw New ArgumentOutOfRangeException(NameOf(age), "Age unrealistic")
End If
End Sub
' throw with inner exception (wrapping)
Function LoadConfig(path As String) As String
Try
Return File.ReadAllText(path)
Catch ex As FileNotFoundException
Throw New ConfigurationException("Config not found", ex) ' wrap
End Try
End Function
' custom exception (inherit from Exception)
<Serializable>
Public Class InvalidTransactionException
Inherits Exception
Public Property TransactionId As String
Public Sub New(message As String, transactionId As String)
MyBase.New(message)
Me.TransactionId = transactionId
End Sub
Public Sub New(message As String, transactionId As String, inner As Exception)
MyBase.New(message, inner)
Me.TransactionId = transactionId
End Sub
Public Overrides Function ToString() As String
Return $"Transaction {TransactionId} failed: {Message}"
End Function
End Class
' using the custom exception
Sub ProcessPayment(amount As Decimal, txnId As String)
If amount <= 0 Then
Throw New InvalidTransactionException("Amount must be positive", txnId)
End If
' ... process
End Sub
' catch custom exception
Try
ProcessPayment(-100, "TXN-001")
Catch ex As InvalidTransactionException
Console.WriteLine($"Failed: {ex.TransactionId} - {ex.Message}")
End TryUsing 语句与 IDisposable
Using 确保在块退出时调用 Dispose() —— 对不会及时垃圾回收的资源(文件、数据库连接、网络流)至关重要。可以在一个 Using 中声明多个资源(逗号分隔)。当你的类持有非托管资源或其他 IDisposable 对象时,实现 IDisposable。Dispose 模式:Dispose(disposing As Boolean) 释放托管(disposing=True 时)和非托管资源;GC.SuppressFinalize 防止终结器运行。始终将 IDisposable 对象包装在 Using 中以防止资源泄漏。
Imports System.IO
' Using ensures Dispose is called (even on exception)
Using writer As New StreamWriter("output.txt")
writer.WriteLine("Hello")
writer.WriteLine("World")
End Using ' writer.Dispose() called automatically
' equivalent to:
Dim writer2 As StreamWriter = Nothing
Try
writer2 = New StreamWriter("output.txt")
writer2.WriteLine("Hello")
Finally
writer2?.Dispose()
End Using
' multiple resources in one Using
Using conn As New SqlClient.SqlConnection(connStr),
cmd As New SqlClient.SqlCommand("SELECT 1", conn)
conn.Open()
Dim result = cmd.ExecuteScalar()
End Using
' implement IDisposable for your class
Public Class FileManager
Implements IDisposable
Private stream As FileStream
Private disposed As Boolean = False
Public Sub New(path As String)
stream = New FileStream(path, FileMode.Open)
End Sub
Public Function ReadLine() As String
' ... read from stream
End Function
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposed Then
If disposing Then
' free managed resources
stream?.Dispose()
End If
' free unmanaged resources (if any)
disposed = True
End If
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
End Class
' usage
Using fm As New FileManager("data.txt")
Dim line = fm.ReadLine()
End Using异常最佳实践
异常最佳实践:(1) 不要捕获你无法实际处理的异常 —— 让它们传播。(2) 切勿静默吞掉异常(空 Catch)—— 至少记录它们。(3) 对用户输入使用 TryParse 而非 Parse(异常代价高昂,且用于真正异常的情况)。(4) 抛出特定异常类型(ArgumentOutOfRangeException,而非 Exception)。(5) 使用 When 过滤器在不捕获的情况下记录(返回 False 表示不捕获)。(6) 使用 Throw(裸)重新抛出以保留堆栈跟踪 —— 而非 Throw ex,后者会重置它。异常用于异常情况,而非正常控制流。
' 1. Don't catch exceptions you can't handle
' BAD: swallows all errors
Try
File.Delete(path)
Catch ex As Exception
' silent failure — bug hidden!
End Try
' GOOD: only catch what you expect
Try
File.Delete(path)
Catch ex As FileNotFoundException
' file already gone — that's fine
Catch ex As UnauthorizedAccessException
Console.WriteLine("Permission denied: " & ex.Message)
' re-throw or handle meaningfully
End Try
' 2. Log exceptions (don't swallow)
Try
ProcessData()
Catch ex As Exception
Logger.Error(ex, "ProcessData failed")
Throw ' re-throw after logging
End Try
' 3. Validate input (avoid exceptions for expected conditions)
' BAD: exception for control flow
Try
Dim n = Integer.Parse(input)
Catch ex As FormatException
n = 0
End Try
' GOOD: TryParse
Dim n As Integer
If Not Integer.TryParse(input, n) Then
n = 0
Console.WriteLine("Invalid number, using 0")
End If
' 4. Use specific exception types
If amount < 0 Then
Throw New ArgumentOutOfRangeException(NameOf(amount))
End If
' 5. Exception filters (When) for logging without catching
Try
RiskyOperation()
Catch ex As Exception When LogError(ex)
' only enters if LogError returns True
End Try
Function LogError(ex As Exception) As Boolean
Logger.Error(ex)
Return False ' don't catch, just log
End Function调试与诊断
Debug.Assert/WriteLine 仅在 Debug 构建中运行(在 Release 中被编译掉)—— 用于不变量和诊断。Trace 在两种构建中都有效 —— 用于生产日志。#If DEBUG...#End If 启用条件编译。Debugger.Break() 充当编程式断点。Stopwatch 精确测量经过的时间(用于基准测试)。StackTrace 捕获调用链(用于日志记录)。EventLog 写入 Windows 事件日志(创建源需要管理员权限)。使用这些工具诊断问题而不修改生产行为。
Imports System.Diagnostics
' Debug.Assert: only in Debug builds
Debug.Assert(age >= 0, "Age should be non-negative")
' Debug.WriteLine: only in Debug builds
Debug.WriteLine($"Processing {name}, age {age}")
' Trace: works in Release builds too
Trace.WriteLine("App started")
Trace.WriteLineIf(age > 100, "Unusual age")
' conditional compilation
#If DEBUG Then
Console.WriteLine("Debug build")
#Else
Console.WriteLine("Release build")
#End If
' Debugger.Break: pause in debugger (like a breakpoint)
If count > 1000 Then
Debugger.Break()
End If
' Stopwatch for performance measurement
Dim sw As Stopwatch = Stopwatch.StartNew()
For i As Integer = 1 To 1000000
' some work
Next
sw.Stop()
Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds} ms")
' StackTrace for debugging
Dim st As New StackTrace(True)
Console.WriteLine(st.ToString()) ' current call stack
' EventLog for Windows event logging (requires admin)
If Not EventLog.SourceExists("MyApp") Then
EventLog.CreateEventSource("MyApp", "Application")
End If
EventLog.WriteEntry("MyApp", "Started", EventLogEntryType.Information)
' process info
Dim p As Process = Process.GetCurrentProcess()
Console.WriteLine($"Memory: {p.WorkingSet64 \ 1024} KB")
Console.WriteLine($"PID: {p.Id}")文件 I/O 与流
文本文件读写
File.WriteAllText/ReadAllText 对小文件最简单。File.ReadAllLines 返回 String 数组(将整个文件加载到内存)。对于大文件,使用 StreamReader/StreamWriter 配合 Using —— 它们逐行读/写,保持低内存。Using 确保即使在异常时流也会关闭。AppendAllText/AppendAllLines 添加到现有文件。读取前始终检查 File.Exists 以给出友好错误。对于异步 I/O(非阻塞 UI),使用 ReadAllTextAsync/WriteAllTextAsync 配合 Await。
Imports System.IO
' write all text at once (simple)
File.WriteAllText("output.txt", "Hello, World!")
' append text
File.AppendAllText("log.txt", $"[{Date.Now}] Started{Environment.NewLine}")
' read all text
Dim content As String = File.ReadAllText("output.txt")
' read all lines into an array
Dim lines() As String = File.ReadAllLines("data.csv")
For Each line In lines
Console.WriteLine(line)
Next
' write all lines
File.WriteAllLines("nums.txt", {"one", "two", "three"})
' append lines
File.AppendAllLines("log.txt", {"line1", "line2"})
' stream-based writing (for large files)
Using writer As New StreamWriter("big.txt")
For i As Integer = 1 To 1000000
writer.WriteLine($"Line {i}")
Next
End Using ' flushes and closes automatically
' stream-based reading (line by line, memory-efficient)
Using reader As New StreamReader("big.txt")
Dim line As String
Do
line = reader.ReadLine()
If line Is Nothing Then Exit Do
Console.WriteLine(line)
Loop
End Using
' check if file exists
If File.Exists("data.txt") Then
Console.WriteLine("Found")
End If
' File.ReadAllLinesAsync (VB 15+ with async)
Async Function ReadAsync(path As String) As Task(Of String())
Return Await File.ReadAllLinesAsync(path)
End Function二进制文件与序列化
BinaryWriter/Reader 以紧凑的二进制格式读/写原始类型 —— 读取顺序必须匹配写入顺序。对于结构化数据,优先使用 JSON(人类可读、语言无关)。System.Text.Json(内置、快速)或 Newtonsoft.Json(流行、功能丰富)将对象序列化到/从 JSON。JsonSerializer.Serialize/Deserialize(Of T) 是核心方法。使用 JsonSerializerOptions 进行格式化(缩进、camelCase)。JSON 非常适合配置文件、API 和数据交换。避免使用 BinaryFormatter(安全风险 —— 在 .NET 5+ 中已移除)。
Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary
' write binary data
Using fs As New FileStream("data.bin", FileMode.Create)
Using writer As New BinaryWriter(fs)
writer.Write(42) ' Integer
writer.Write(3.14) ' Double
writer.Write("Hello") ' String (length-prefixed)
writer.Write(True) ' Boolean
writer.Write(New Byte() {1, 2, 3}) ' byte array
End Using
End Using
' read binary data (must read in same order!)
Using fs As New FileStream("data.bin", FileMode.Open)
Using reader As New BinaryReader(fs)
Dim n As Integer = reader.ReadInt32()
Dim d As Double = reader.ReadDouble()
Dim s As String = reader.ReadString()
Dim b As Boolean = reader.ReadBoolean()
Dim bytes() As Byte = reader.ReadBytes(3)
Console.WriteLine($"{n}, {d}, {s}, {b}")
End Using
End Using
' JSON serialization (Newtonsoft.Json or System.Text.Json)
Imports System.Text.Json
Public Class User
Public Property Name As String
Public Property Age As Integer
End Class
Dim u As New User With {.Name = "Alice", .Age = 30}
Dim json As String = JsonSerializer.Serialize(u)
' {"Name":"Alice","Age":30}
File.WriteAllText("user.json", json)
Dim restored As User = JsonSerializer.Deserialize(Of User)(json)
' JSON with options
Dim opts As New JsonSerializerOptions With {
.WriteIndented = True,
.PropertyNamingPolicy = JsonNamingPolicy.CamelCase
}
Dim prettyJson = JsonSerializer.Serialize(u, opts)目录与路径操作
Directory.CreateDirectory/GetFiles/GetDirectories/Exists/Delete 管理文件夹。GetFiles 支持搜索模式(*.csv)和 SearchOption.AllDirectories 进行递归。Path 类跨平台安全地处理路径 —— 始终使用 Path.Combine(而非 &)连接路径(处理分隔符)。Path.GetTempFileName 创建唯一的临时文件。FileInfo/DirectoryInfo 是文件/目录操作的面向对象包装器,具有属性(Length、CreationTime)和方法(CopyTo、MoveTo、Delete)。使用这些而非字符串操作处理路径。
Imports System.IO
' directory operations
Directory.CreateDirectory("backup/2024/june")
' list files
Dim files() As String = Directory.GetFiles("C:\temp")
Dim csvFiles() As String = Directory.GetFiles("C:\temp", "*.csv")
Dim allFiles() As String = Directory.GetFiles("C:\temp", "*.*", SearchOption.AllDirectories)
' list directories
Dim dirs() As String = Directory.GetDirectories("C:\temp")
' check and delete
If Directory.Exists("old") Then
Directory.Delete("old", recursive := True) ' delete with contents
End If
' Path class (cross-platform path handling)
Dim fullPath As String = Path.Combine("folder", "sub", "file.txt")
' folder\sub\file.txt (on Windows)
Console.WriteLine(Path.GetFileName("C:\temp\data.txt")) ' data.txt
Console.WriteLine(Path.GetExtension("photo.JPG")) ' .JPG
Console.WriteLine(Path.GetFileNameWithoutExtension("data.txt")) ' data
Console.WriteLine(Path.GetDirectoryName("C:\temp\data.txt")) ' C:\temp
Console.WriteLine(Path.GetFullPath("data.txt")) ' absolute path
' temp files
Dim tempFile As String = Path.GetTempFileName() ' creates a temp file
Dim tempDir As String = Path.GetTempPath()
Console.WriteLine(tempFile)
' file info
Dim fi As New FileInfo("data.txt")
If fi.Exists Then
Console.WriteLine($"Size: {fi.Length} bytes")
Console.WriteLine($"Created: {fi.CreationTime}")
Console.WriteLine($"Modified: {fi.LastWriteTime}")
Console.WriteLine($"Extension: {fi.Extension}")
fi.CopyTo("data_backup.txt", overwrite := True)
fi.MoveTo("renamed.txt")
fi.Delete()
End If
' directory info
Dim di As New DirectoryInfo("C:\temp")
For Each f As FileInfo In di.GetFiles("*.txt")
Console.WriteLine(f.Name)
NextCSV 解析与数据文件
对于简单 CSV,Split(","c) 有效 —— 但它在带逗号的引号字段("Smith, John")上会出错。TextFieldParser(在 Microsoft.VisualBasic.FileIO 中)正确处理引号、转义引号和分隔符 —— 用于真正的 CSV。它还解析固定宽度文件(FieldWidths)。对于写入 CSV,手动引用包含逗号/引号的字段。My.Computer.FileSystem 是 VB 特有的便捷包装器(更简单的 API,更少的控制)。对于大型 CSV,考虑 CsvHelper(NuGet)—— 一个健壮的、流式 CSV 库。始终单独处理标题行。
Imports System.IO
Imports Microsoft.VisualBasic.FileIO
' simple CSV split (doesn't handle quoted commas)
Dim lines() As String = File.ReadAllLines("data.csv")
For Each line In lines
Dim fields() As String = line.Split(","c)
Console.WriteLine($"{fields(0)}, {fields(1)}")
Next
' robust CSV with TextFieldParser (handles quotes, commas)
Using parser As New TextFieldParser("data.csv")
parser.TextFieldType = FieldType.Delimited
parser.Delimiters = New String() {","}
parser.HasFieldsEnclosedInQuotes = True
' skip header
If Not parser.EndOfData Then parser.ReadLine()
While Not parser.EndOfData
Dim fields() As String = parser.ReadFields()
Console.WriteLine($"Name: {fields(0)}, Age: {fields(1)}")
End While
End Using
' write CSV
Using writer As New StreamWriter("output.csv")
writer.WriteLine("Name,Age,City")
writer.WriteLine("Alice,30,NYC")
writer.WriteLine("Bob,25,LA")
' quote fields with commas
writer.WriteLine($"""Smith, John"",40,Chicago")
End Using
' parse fixed-width files
Using parser As New TextFieldParser("fixed.txt")
parser.TextFieldType = FieldType.FixedWidth
parser.FieldWidths = New Integer() {10, 5, 20}
End Using
' read with My.Computer.FileSystem (VB-specific shortcut)
Dim contents As String = My.Computer.FileSystem.ReadAllText("data.txt")
Dim allLines As Object = My.Computer.FileSystem.ReadAllBytes("data.bin")
' write bytes
File.WriteAllBytes("data.bin", New Byte() {1, 2, 3, 4, 5})异步文件操作
异步文件 I/O 使用 ReadToEndAsync/ReadLineAsync/WriteAsync 配合 Await —— 线程在 I/O 期间不被阻塞,提高响应性(尤其是在 UI 应用中)。异步方法返回 Task 或 Task(Of T);Await 解包结果。对于并发处理多个文件,启动所有任务并 Await Task.WhenAll(并行 I/O)。Async Sub 用于事件处理器;Async Function 用于其他所有情况。异步 I/O 在 Web 服务器(处理许多请求)和桌面应用(保持 UI 响应)中表现出色。开销很小,因此对任何可能缓慢的 I/O 使用异步。
Imports System.IO
Imports System.Threading.Tasks
' async read (non-blocking)
Async Function ReadFileAsync(path As String) As Task(Of String)
Using reader As New StreamReader(path)
Return Await reader.ReadToEndAsync()
End Using
End Function
' async write
Async Function WriteFileAsync(path As String, content As String) As Task
Using writer As New StreamWriter(path)
Await writer.WriteAsync(content)
End Using
End Function
' async line-by-line (memory-efficient for large files)
Async Function ProcessLargeFileAsync(path As String) As Task
Using reader As New StreamReader(path)
Dim line As String
Do
line = Await reader.ReadLineAsync()
If line Is Nothing Then Exit Do
' process line
Console.WriteLine(line)
Loop
End Using
End Function
' call async methods (must be in an Async Sub/Function)
Async Sub Main()
Dim content As String = Await ReadFileAsync("data.txt")
Console.WriteLine(content)
Await WriteFileAsync("output.txt", "Async write")
End Sub
' parallel file processing
Async Function ProcessFilesAsync(paths As String()) As Task
Dim tasks As New List(Of Task)
For Each path In paths
tasks.Add(ProcessFileAsync(path))
Next
Await Task.WhenAll(tasks) ' wait for all
End Function
Async Function ProcessFileAsync(path As String) As Task
Dim content = Await File.ReadAllTextAsync(path)
' process content
End Function
' File.ReadAllLinesAsync (.NET Core+)
Async Function CountLinesAsync(path As String) As Task(Of Integer)
Dim lines = Await File.ReadAllLinesAsync(path)
Return lines.Length
End FunctionWindows Forms 与 UI
窗体基础与事件
Windows Forms(WinForms)通过向 Form 添加 Controls 构建桌面 UI。每个控件都有属性(Text、Location、Size)和事件(Click、Load、FormClosing)。AddHandler 在运行时连接事件;Handles(配合 WithEvents)在编译时连接。MessageBox.Show 显示对话框。OnLoad/OnFormClosing 是可重写的受保护方法,用于窗体生命周期。Application.Run 启动消息循环。WinForms 使用设计器(在 Visual Studio 中拖放)—— 生成的代码放在 .Designer.vb 分部类中。对于现代应用,考虑 WPF 或 WinUI。
Imports System.Windows.Forms
Imports System.Drawing
Public Class MainForm
Inherits Form
Public Sub New()
Me.Text = "My App" ' form title
Me.Size = New Size(400, 300)
Me.StartPosition = FormStartPosition.CenterScreen
' create a button
Dim btn As New Button With {
.Text = "Click Me",
.Location = New Point(150, 100),
.Size = New Size(100, 30)
}
AddHandler btn.Click, AddressOf OnButtonClick
Me.Controls.Add(btn)
' create a label
Dim lbl As New Label With {
.Text = "Hello",
.Location = New Point(150, 50),
.AutoSize = True
}
Me.Controls.Add(lbl)
End Sub
Private Sub OnButtonClick(sender As Object, e As EventArgs)
MessageBox.Show("Button clicked!", "Info",
MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
' form events
Protected Overrides Sub OnLoad(e As EventArgs)
MyBase.OnLoad(e)
Console.WriteLine("Form loaded")
End Sub
Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs)
Dim result = MessageBox.Show("Close?", "Confirm",
MessageBoxButtons.YesNo)
If result = DialogResult.No Then
e.Cancel = True
End If
MyBase.OnFormClosing(e)
End Sub
End Class
' run the app
Module Program
Sub Main()
Application.EnableVisualStyles()
Application.Run(New MainForm())
End Sub
End Module常用控件
常用 WinForms 控件:TextBox(文本输入,Multiline 用于区域)、CheckBox(布尔)、RadioButton(互斥 —— 在 GroupBox 中分组)、ComboBox(下拉)、NumericUpDown(带微调器的数字输入)、ListBox(可选列表)。设置 Location(Point)和 Size。使用对象初始化器(With {.Prop = value})进行简洁设置。Controls.AddRange 一次添加多个控件。每个控件都有默认事件(Button.Click、TextBox.TextChanged、ComboBox.SelectedIndexChanged)—— 在设计器中双击生成处理器。
Imports System.Windows.Forms
Imports System.Drawing
Public Class ControlsForm
Inherits Form
Public Sub New()
' TextBox (single-line input)
Dim txt As New TextBox With {
.Location = New Point(20, 20),
.Width = 200
}
' multiline TextBox
Dim txtMulti As New TextBox With {
.Multiline = True,
.Location = New Point(20, 60),
.Size = New Size(200, 80),
.ScrollBars = ScrollBars.Vertical
}
' CheckBox
Dim chk As New CheckBox With {
.Text = "Enable feature",
.Location = New Point(20, 160),
.Checked = True
}
' RadioButton (group in GroupBox for mutual exclusion)
Dim grp As New GroupBox With {.Text = "Gender", .Location = New Point(20, 190), .Size = New Size(120, 80)}
Dim rb1 As New RadioButton With {.Text = "Male", .Location = New Point(10, 20), .Checked = True}
Dim rb2 As New RadioButton With {.Text = "Female", .Location = New Point(10, 40)}
grp.Controls.AddRange({rb1, rb2})
' ComboBox (dropdown)
Dim cmb As New ComboBox With {.Location = New Point(150, 190), .DropDownStyle = ComboBoxStyle.DropDownList}
cmb.Items.AddRange({"Red", "Green", "Blue"})
cmb.SelectedIndex = 0
' NumericUpDown
Dim num As New NumericUpDown With {
.Location = New Point(150, 220),
.Minimum = 0,
.Maximum = 100,
.Value = 50
}
' ListBox
Dim lst As New ListBox With {.Location = New Point(20, 280), .Size = New Size(150, 80)}
lst.Items.AddRange({"Apple", "Banana", "Cherry"})
Me.Controls.AddRange({txt, txtMulti, chk, grp, cmb, num, lst})
End Sub
End Class布局:面板、停靠与锚定
WinForms 布局:Dock(Top/Bottom/Left/Right/Fill)使控件填充一条边 —— 最后添加 Fill 使其占据剩余空间。Anchor 将控件固定到父边缘(窗体调整大小时它也调整大小)。TableLayoutPanel 在网格中排列控件(带百分比/绝对大小的行/列)—— 最适合表单。FlowLayoutPanel 在流中堆叠控件(自动换行)。Panel 是用于分组的简单容器。对于响应式布局,优先使用 TableLayoutPanel 而非手动定位。Controls.Add 的顺序对停靠很重要(后添加的覆盖先添加的)。
Imports System.Windows.Forms
Imports System.Drawing
Public Class LayoutForm
Inherits Form
Public Sub New()
Me.Size = New Size(600, 400)
' Panel: container for grouping controls
Dim topPanel As New Panel With {
.Dock = DockStyle.Top, ' fill the top
.Height = 50,
.BackColor = Color.LightBlue
}
Dim bottomPanel As New Panel With {
.Dock = DockStyle.Bottom,
.Height = 40,
.BackColor = Color.LightGray
}
' Fill remaining space
Dim centerPanel As New Panel With {
.Dock = DockStyle.Fill,
.BackColor = Color.White
}
' Add in reverse Z-order (Fill last to actually fill)
Me.Controls.Add(centerPanel)
Me.Controls.Add(bottomPanel)
Me.Controls.Add(topPanel)
' Anchoring: control resizes with parent edges
Dim btn As New Button With {
.Text = "Anchored",
.Location = New Point(10, 10),
.Anchor = AnchorStyles.Top Or AnchorStyles.Left Or AnchorStyles.Right
}
centerPanel.Controls.Add(btn)
' TableLayoutPanel: grid layout
Dim tlp As New TableLayoutPanel With {
.Dock = DockStyle.Fill,
.ColumnCount = 2,
.RowCount = 2
}
tlp.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 50))
tlp.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 50))
tlp.Controls.Add(New Label With {.Text = "Name:"}, 0, 0)
tlp.Controls.Add(New TextBox(), 1, 0)
tlp.Controls.Add(New Label With {.Text = "Age:"}, 0, 1)
tlp.Controls.Add(New NumericUpDown(), 1, 1)
' FlowLayoutPanel: stacks controls left-to-right or top-to-bottom
Dim flp As New FlowLayoutPanel With {
.FlowDirection = FlowDirection.LeftToRight,
.Dock = DockStyle.Top,
.Height = 40
}
flp.Controls.AddRange({New Button With {.Text = "New"}, New Button With {.Text = "Open"}})
Me.Controls.Add(flp)
Me.Controls.Add(tlp)
End Sub
End Class对话框:OpenFileDialog、SaveFileDialog、ColorDialog
常用对话框:OpenFileDialog(选择要打开的文件)、SaveFileDialog(选择保存位置)、ColorDialog(选择颜色)、FontDialog(选择字体)、FolderBrowserDialog(选择目录)。Filter 使用格式"Description|pattern|Description|pattern"。ShowDialog 返回 DialogResult(OK/Cancel)—— 使用结果前始终检查。将对话框包装在 Using 中(它们是 IDisposable)。OverwritePrompt 防止意外覆盖。InitialDirectory 在有用的位置启动对话框。这些对话框无需自定义代码即可提供原生 Windows UI。
Imports System.Windows.Forms
Public Class DialogForm
Inherits Form
Private Sub OpenFile()
Using ofd As New OpenFileDialog
ofd.Title = "Select a file"
ofd.Filter = "Text files (*.txt)|*.txt|CSV files (*.csv)|*.csv|All files (*.*)|*.*"
ofd.FilterIndex = 1
ofd.Multiselect = False
ofd.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
If ofd.ShowDialog() = DialogResult.OK Then
Dim path As String = ofd.FileName
MessageBox.Show($"Selected: {path}")
End If
End Using
End Sub
Private Sub SaveFile()
Using sfd As New SaveFileDialog
sfd.Filter = "Text files (*.txt)|*.txt|All files (*.*)|*.*"
sfd.DefaultExt = "txt"
sfd.AddExtension = True
sfd.OverwritePrompt = True ' ask before overwriting
If sfd.ShowDialog() = DialogResult.OK Then
System.IO.File.WriteAllText(sfd.FileName, "Saved content")
End If
End Using
End Sub
Private Sub PickColor()
Using cd As New ColorDialog
cd.AllowFullOpen = True
cd.FullOpen = True
cd.Color = Color.Red
If cd.ShowDialog() = DialogResult.OK Then
Me.BackColor = cd.Color
End If
End Using
End Sub
Private Sub PickFont()
Using fd As New FontDialog
fd.ShowColor = True
fd.Font = Me.Font
If fd.ShowDialog() = DialogResult.OK Then
Me.Font = fd.Font
End If
End Using
End Sub
Private Sub PickFolder()
Using fbd As New FolderBrowserDialog
fbd.Description = "Select a folder"
fbd.SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
If fbd.ShowDialog() = DialogResult.OK Then
MessageBox.Show($"Folder: {fbd.SelectedPath}")
End If
End Using
End Sub
End Class数据绑定与 DataGridView
数据绑定自动将 UI 控件连接到数据对象。简单绑定:txt.DataBindings.Add("Text", obj, "PropName")。对于列表,将 DataGridView.DataSource 设置为 BindingList(Of T) 或 DataTable。INotifyPropertyChanged 启用双向绑定 —— 属性更改时 UI 更新(在 setter 中引发 PropertyChanged)。BindingList(Of T) 类似 ObservableCollection —— 添加/删除项时通知网格。BindingSource 添加导航和过滤。AutoGenerateColumns 从属性创建列。数据绑定消除了 UI 和数据之间的手动同步代码。
Imports System.Windows.Forms
Imports System.ComponentModel
Public Class User
Implements INotifyPropertyChanged
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Private _name As String
Public Property Name As String
Get
Return _name
End Get
Set(value As String)
If _name <> value Then
_name = value
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(NameOf(Name)))
End If
End Set
End Property
Public Property Age As Integer
End Class
Public Class DataForm
Inherits Form
Public Sub New()
' simple binding: TextBox <-> object property
Dim user As New User With {.Name = "Alice", .Age = 30}
Dim txt As New TextBox With {.Location = New Point(20, 20)}
txt.DataBindings.Add("Text", user, "Name") ' two-way binding
' DataGridView with a list
Dim people As New BindingList(Of User) From {
New User With {.Name = "Alice", .Age = 30},
New User With {.Name = "Bob", .Age = 25}
}
Dim dgv As New DataGridView With {
.Location = New Point(20, 60),
.Size = New Size(350, 200),
.AutoGenerateColumns = True,
.AllowUserToAddRows = True,
.AllowUserToDeleteRows = True,
.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill
}
dgv.DataSource = people ' bind to list
' BindingSource for navigation
Dim bs As New BindingSource With {.DataSource = people}
dgv.DataSource = bs
' DataTable binding
Dim dt As New DataTable()
dt.Columns.Add("Name", GetType(String))
dt.Columns.Add("Age", GetType(Integer))
dt.Rows.Add("Carol", 28)
dt.Rows.Add("Dave", 35)
dgv.DataSource = dt
Me.Controls.Add(txt)
Me.Controls.Add(dgv)
End Sub
End Class文件 I/O 深入
StreamReader 与 StreamWriter
StreamReader/StreamWriter 处理具有正确编码和处置的文本文件。Using 块确保即使在异常时文件也会关闭 —— 始终使用它。File.ReadAllLines/ReadAllText 对小文件很方便;循环中的 ReadLine 对大文件节省内存。异步方法(ReadAllTextAsync)防止 I/O 期间 UI 冻结。带 append:=True 的 StreamWriter 添加到现有文件。默认编码为 UTF-8;对于旧格式,使用 New StreamWriter(path, append, Encoding.UTF8) 显式指定。
Imports System.IO
' write text file
Using writer As New StreamWriter("output.txt")
writer.WriteLine("First line")
writer.WriteLine("Second line")
writer.Write("No newline ")
writer.WriteLine("appended")
End Using ' automatically disposes/closes
' append to file
Using writer As New StreamWriter("log.txt", append:=True)
writer.WriteLine($"{Date.Now}: Application started")
End Using
' read all lines
Dim lines() As String = File.ReadAllLines("input.txt")
For Each line In lines
Console.WriteLine(line)
Next
' read line by line (memory efficient for big files)
Using reader As New StreamReader("big.txt")
Dim line As String
Do While reader.Peek() >= 0
line = reader.ReadLine()
' process line
Loop
End Using
' read all text at once
Dim content As String = File.ReadAllText("input.txt")
' async I/O (non-blocking)
Dim text As String = Await File.ReadAllTextAsync("input.txt")
Await File.WriteAllTextAsync("output.txt", "Hello")二进制与 CSV 文件处理
BinaryWriter/Reader 以紧凑的二进制格式存储原始类型 —— 比文本小得多且解析更快。始终以与写入相同的顺序和类型读取。对于 CSV,TextFieldParser 正确处理带嵌入逗号的引号字段(不同于简单的 Split)。对于生产 CSV,使用 CsvHelper(NuGet)库,它处理边缘情况、类型映射和流式处理。二进制文件是平台特定的(字节序);使用 BinaryWriter 配合小端序以实现跨平台兼容性。
Imports System.IO
' binary file with BinaryWriter/Reader
Using fs As New FileStream("data.bin", FileMode.Create),
bw As New BinaryWriter(fs)
bw.Write(42) ' Integer
bw.Write(3.14) ' Double
bw.Write("Hello") ' String (length-prefixed)
bw.Write(True) ' Boolean
End Using
Using fs As New FileStream("data.bin", FileMode.Open),
br As New BinaryReader(fs)
Dim num As Integer = br.ReadInt32()
Dim pi As Double = br.ReadDouble()
Dim s As String = br.ReadString()
Dim flag As Boolean = br.ReadBoolean()
End Using
' CSV parsing (manual)
Dim csvLines = File.ReadAllLines("data.csv")
For Each line In csvLines.Skip(1) ' skip header
Dim fields = line.Split(","c)
Dim name = fields(0)
Dim age = Integer.Parse(fields(1))
Next
' CSV with TextFieldParser (handles quoted fields)
Using parser As New Microsoft.VisualBasic.FileIO.TextFieldParser("data.csv")
parser.TextFieldType = FileIO.FieldType.Delimited
parser.SetDelimiters(",")
While Not parser.EndOfData
Dim fields() As String = parser.ReadFields()
End While
End Using文件系统操作
File 和 FileInfo 提供文件操作;Directory 和 DirectoryInfo 处理文件夹。当需要多个属性时,FileInfo/DirectoryInfo 更高效(一次系统调用)。EnumerateFiles 是惰性的(一次产生一个)vs GetFiles 将所有路径加载到内存 —— 对大型目录使用 Enumerate。Path.Combine 使用正确的分隔符跨平台构建路径;切勿手动连接字符串。SearchOption.AllDirectories 在访问被拒绝时可能抛出;捕获 UnauthorizedAccessException 或使用递归辅助方法。
Imports System.IO
' file operations
File.Copy("source.txt", "dest.txt", overwrite:=True)
File.Move("old.txt", "new.txt")
File.Delete("unwanted.txt")
File.Exists("check.txt") ' Boolean
' file info
Dim fi As New FileInfo("data.txt")
Console.WriteLine($"Size: {fi.Length} bytes")
Console.WriteLine($"Created: {fi.CreationTime}")
Console.WriteLine($"Modified: {fi.LastWriteTime}")
Console.WriteLine($"Extension: {fi.Extension}")
' directory operations
Directory.CreateDirectory("path o
ewdir")
Directory.Exists("somedir")
Directory.Delete("dir", recursive:=True)
' enumerate files (lazy, memory-efficient)
For Each path In Directory.EnumerateFiles("C:data", "*.txt", SearchOption.AllDirectories)
Console.WriteLine(path)
Next
' directory info
Dim di As New DirectoryInfo("C:data")
For Each file In di.GetFiles("*.csv")
Console.WriteLine($"{file.Name} - {file.Length}")
Next
' path operations
Path.Combine("C:", "data", "file.txt") ' "C:dataile.txt"
Path.GetExtension("file.txt") ' ".txt"
Path.GetFileNameWithoutExtension("file.txt") ' "file"
Path.GetTempFileName() ' temp file path序列化(JSON 与 XML)
System.Text.Json(现代、快速)优于旧版 DataContractJsonSerializer。JsonSerializer.Serialize/Deserialize 处理大多数类型;使用 JsonSerializerOptions 进行格式化和命名策略。对于 XML,XmlSerializer 简单但有限(无字典,需要无参构造函数)。对于复杂场景(多态、循环引用),考虑通过 NuGet 使用 Newtonsoft.Json(Json.NET)—— 功能更多但更慢。对于不可信输入,始终处理反序列化错误(JsonException)。
Imports System.Text.Json
' define a class
Public Class Person
Public Property Name As String
Public Property Age As Integer
Public Property Email As String
End Class
' JSON serialize
Dim p As New Person With {.Name = "Alice", .Age = 30, .Email = "[email protected]"}
Dim json As String = JsonSerializer.Serialize(p)
File.WriteAllText("person.json", json)
' JSON with options
Dim opts As New JsonSerializerOptions With {
.WriteIndented = True,
.PropertyNamingPolicy = JsonNamingPolicy.CamelCase
}
json = JsonSerializer.Serialize(p, opts)
' JSON deserialize
Dim text = File.ReadAllText("person.json")
Dim p2 = JsonSerializer.Deserialize(Of Person)(text)
' serialize a list
Dim people = New List(Of Person) From {p, p2}
json = JsonSerializer.Serialize(people)
' XML serialization
Imports System.Xml.Serialization
Dim xs As New XmlSerializer(GetType(Person))
Using fs As New FileStream("person.xml", FileMode.Create)
xs.Serialize(fs, p)
End Using
Using fs As New FileStream("person.xml", FileMode.Open)
Dim p3 = CType(xs.Deserialize(fs), Person)
End Using异步文件操作与流
异步文件 I/O 在大型操作期间保持 UI 响应。ReadAsync/WriteAsync 使用缓冲区进行细粒度进度报告。IProgress(Of T) 安全地将进度报告回 UI 线程。MemoryStream 用于内存数据(无磁盘)。GZipStream 即时压缩/解压缩流。对于网络 I/O,使用 HttpClient(异步)。始终使用 Using 块确保流关闭。缓冲区大小 81920(80KB)是良好的默认值 —— 平衡内存和系统调用开销。
Imports System.IO
' async copy with progress
Async Function CopyWithProgress(src As String, dest As String, progress As IProgress(Of Long)) As Task
Const bufferSize As Integer = 81920
Dim buffer(bufferSize - 1) As Byte
Using srcStream As FileStream = File.OpenRead(src),
destStream As FileStream = File.Create(dest)
Dim read As Integer
Dim total As Long = 0
Do
read = Await srcStream.ReadAsync(buffer, 0, bufferSize)
If read = 0 Then Exit Do
Await destStream.WriteAsync(buffer, 0, read)
total += read
progress?.Report(total)
Loop
End Using
End Function
' usage with progress bar
Dim prog As New Progress(Of Long)(Sub(bytes)
ProgressBar1.Value = CInt(bytes * 100 totalSize)
End Sub)
Await CopyWithProgress("big.zip", "copy.zip", prog)
' memory stream (in-memory)
Using ms As New MemoryStream()
Dim bw As New BinaryWriter(ms)
bw.Write("data")
ms.Position = 0
' read back
End Using
' GZip compression
Imports System.IO.Compression
Using src = File.OpenRead("data.txt"),
dest = File.Create("data.txt.gz"),
gz As New GZipStream(dest, CompressionMode.Compress)
src.CopyTo(gz)
End UsingADO.NET 数据库访问
连接与命令
始终使用参数化查询(Parameters.AddWithValue)防止 SQL 注入 —— 切勿将用户输入连接到 SQL 字符串中。SqlConnection、SqlCommand 和 SqlDataReader 都是 IDisposable;将它们包装在 Using 块中。ExecuteReader 返回行;ExecuteNonQuery 返回受影响的行数(INSERT/UPDATE/DELETE);ExecuteScalar 返回单个值(COUNT、SUM)。对于其他数据库,使用适当的提供程序(OracleConnection、OleDbConnection、OdbcConnection)或通用 DbProviderFactory。
Imports System.Data.SqlClient ' or Microsoft.Data.SqlClient
' connection string (SQL Server)
Dim connStr = "Server=localhost;Database=MyDb;Integrated Security=True;"
' basic query
Using conn As New SqlConnection(connStr)
conn.Open()
Using cmd As New SqlCommand("SELECT Id, Name FROM Users WHERE Age > @age", conn)
cmd.Parameters.AddWithValue("@age", 25)
Using reader As SqlDataReader = cmd.ExecuteReader()
While reader.Read()
Dim id = reader.GetInt32(0)
Dim name = reader.GetString(1)
Console.WriteLine($"{id}: {name}")
End While
End Using
End Using
End Using ' connection auto-closed
' insert with parameters (prevent SQL injection!)
Using conn As New SqlConnection(connStr), _
cmd As New SqlCommand("INSERT INTO Users (Name, Age) VALUES (@name, @age)", conn)
cmd.Parameters.AddWithValue("@name", "Bob")
cmd.Parameters.AddWithValue("@age", 30)
conn.Open()
Dim rowsAffected = cmd.ExecuteNonQuery()
End Using
' scalar query (single value)
Using conn As New SqlConnection(connStr), _
cmd As New SqlCommand("SELECT COUNT(*) FROM Users", conn)
conn.Open()
Dim count = CInt(cmd.ExecuteScalar())
End UsingDataSet 与 DataTable
DataTable/DataSet 是断开连接的数据容器 —— 加载一次,离线工作,稍后更新。SqlDataAdapter.Fill 加载数据;SqlCommandBuilder 为简单的单表场景自动生成 INSERT/UPDATE/DELETE 命令。类型化 DataSet(通过 .xsd 设计器)提供编译时类型检查和 IntelliSense。DataView 过滤和排序而不修改底层 DataTable。对于现代应用,优先使用 Entity Framework 或 Dapper 而非原始 DataSet —— 它们更易维护和测试。DataSet 仍适用于报表和旧版互操作。
Imports System.Data.SqlClient
' fill DataTable
Dim dt As New DataTable()
Using adapter As New SqlDataAdapter("SELECT * FROM Products", connStr)
adapter.Fill(dt)
End Using
' access rows
For Each row As DataRow In dt.Rows
Console.WriteLine(row("Name"))
Console.WriteLine(row.Field(Of Integer)("Price"))
Next
' modify and update
Dim newRow = dt.NewRow()
newRow("Name") = "Widget"
newRow("Price") = 9.99
dt.Rows.Add(newRow)
Using adapter As New SqlDataAdapter("SELECT * FROM Products", connStr),
builder As New SqlCommandBuilder(adapter)
adapter.Update(dt) ' generates INSERT/UPDATE/DELETE
End Using
' typed DataSet (strongly typed)
' add .xsd file to project, drag tables from Server Explorer
' Dim ta As New ProductsTableAdapter()
' Dim products = ta.GetData()
' For Each p In products
' Console.WriteLine(p.ProductName)
' Next
' filter and sort
Dim view As New DataView(dt)
view.RowFilter = "Price > 10"
view.Sort = "Price DESC"
For Each row As DataRowView In view
Console.WriteLine(row("Name"))
Next事务与连接池
事务确保原子性 —— 所有操作成功或全部失败。SqlTransaction 包装一个连接上的多个命令。TransactionScope 启用跨多个连接/资源的分布式事务(需要时使用 MS DTC)—— 调用 scope.Complete() 提交。连接池在 ADO.NET 中是自动的:连接被重用而非重新创建,显著提高性能。始终 Close/Dispose 连接(Using 块)以将其返回池中。对于高吞吐量应用,在连接字符串中配置池大小和生命周期。
Imports System.Data.SqlClient
' explicit transaction
Using conn As New SqlConnection(connStr)
conn.Open()
Using tran As SqlTransaction = conn.BeginTransaction()
Try
Using cmd1 As New SqlCommand(
"UPDATE Accounts SET Balance = Balance - 100 WHERE Id = 1",
conn, tran)
cmd1.ExecuteNonQuery()
End Using
Using cmd2 As New SqlCommand(
"UPDATE Accounts SET Balance = Balance + 100 WHERE Id = 2",
conn, tran)
cmd2.ExecuteNonQuery()
End Using
tran.Commit()
Catch ex As Exception
tran.Rollback()
Throw
End Try
End Using
End Using
' transaction scope (distributed, multi-resource)
Imports System.Transactions
Using scope As New TransactionScope()
' multiple connections, even different databases
Using conn1 As New SqlConnection(connStr1)
' operations on DB1
End Using
Using conn2 As New SqlConnection(connStr2)
' operations on DB2
End Using
scope.Complete() ' commit all
End Using ' rollback if Complete not called
' connection pooling (automatic, configure in connection string)
' "Server=...;Pooling=True;Max Pool Size=100;Connection Lifetime=300"
' connections are reused — always Close/Dispose to return to pool异步数据库操作
异步数据库操作(OpenAsync、ExecuteReaderAsync、ReadAsync)防止长时间查询期间 UI 冻结。该模式镜像同步 ADO.NET 但使用 Await。在 UI 应用中始终使用 Async 以保持响应。对于高吞吐量服务器,异步 DB 调用释放线程处理其他请求。MARS(Multiple Active Result Sets)允许在一个连接上有多个读取器 —— 在连接字符串中启用 'MultipleActiveResultSets=True'。处理 SqlException 以应对数据库特定错误(死锁、约束违规)。
Imports System.Data.SqlClient
' async query
Async Function GetUsersAsync(minAge As Integer) As Task(Of List(Of User))
Dim users As New List(Of User)()
Using conn As New SqlConnection(connStr)
Await conn.OpenAsync()
Using cmd As New SqlCommand("SELECT Id, Name, Age FROM Users WHERE Age > @age", conn)
cmd.Parameters.AddWithValue("@age", minAge)
Using reader = Await cmd.ExecuteReaderAsync()
While Await reader.ReadAsync()
users.Add(New User With {
.Id = reader.GetInt32(0),
.Name = reader.GetString(1),
.Age = reader.GetInt32(2)
})
End While
End Using
End Using
End Using
Return users
End Function
' async execute (INSERT/UPDATE)
Async Function UpdateUserAsync(user As User) As Task(Of Integer)
Using conn As New SqlConnection(connStr)
Await conn.OpenAsync()
Using cmd As New SqlCommand(
"UPDATE Users SET Name=@name, Age=@age WHERE Id=@id", conn)
cmd.Parameters.AddWithValue("@name", user.Name)
cmd.Parameters.AddWithValue("@age", user.Age)
cmd.Parameters.AddWithValue("@id", user.Id)
Return Await cmd.ExecuteNonQueryAsync()
End Using
End Using
End Function
' usage in event handler
Private Async Sub btnLoad_Click(sender As Object, e As EventArgs) Handles btnLoad.Click
btnLoad.Enabled = False
Try
Dim users = Await GetUsersAsync(25)
DataGridView1.DataSource = users
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
btnLoad.Enabled = True
End Try
End SubDapper 微型 ORM
Dapper 是一个微型 ORM,通过扩展方法扩展 IDbConnection —— 快速(接近原始 ADO.NET 速度)且简单。Query<T> 通过将列名匹配到属性自动将行映射到对象。匿名对象提供参数(SQL 注入安全)。批量插入传递一个列表,Dapper 对每个项执行一次。多映射通过在列上拆分行来处理连接。当你想要 SQL 控制但比原始 ADO.NET 更少样板代码时,Dapper 非常理想。对于复杂对象图和变更跟踪,使用 Entity Framework。
Imports Dapper ' NuGet: Install-Package Dapper
Imports System.Data.SqlClient
' simple query
Using conn As New SqlConnection(connStr)
Dim users = conn.Query(Of User)("SELECT * FROM Users WHERE Age > @age",
New With { .age = 25 })
For Each u In users
Console.WriteLine(u.Name)
Next
End Using
' single row
Using conn As New SqlConnection(connStr)
Dim user = conn.QuerySingleOrDefault(Of User)(
"SELECT * FROM Users WHERE Id = @id", New With { .id = 42 })
End Using
' insert
Using conn As New SqlConnection(connStr)
Dim sql = "INSERT INTO Users (Name, Age, Email) VALUES (@Name, @Age, @Email)"
conn.Execute(sql, New With { .Name = "Alice", .Age = 30, .Email = "[email protected]" })
End Using
' bulk insert
Using conn As New SqlConnection(connStr)
Dim users = New List(Of User) From {
New User With {.Name = "A", .Age = 1},
New User With {.Name = "B", .Age = 2}
}
conn.Execute("INSERT INTO Users (Name, Age) VALUES (@Name, @Age)", users)
End Using
' multi-mapping (join)
Dim sql = "SELECT * FROM Orders o INNER JOIN Users u ON o.UserId = u.Id"
Dim orders = conn.Query(Of Order, User, Order)(sql,
Function(o, u)
o.User = u
Return o
End Function, splitOn:="Id")
' stored procedure
Dim result = conn.Query(Of User)("sp_GetActiveUsers",
commandType:=Data.CommandType.StoredProcedure)LINQ to Objects
查询与方法语法
LINQ 提供两种语法:查询(类似 SQL,From...Where...Select)和方法(流式,.Where().Select())。它们编译为相同的 IL —— 根据可读性选择。查询语法支持较少的运算符(无 Sum、Count 直接使用);对这些使用方法语法。LINQ 使用延迟执行 —— 查询在迭代(For Each)或调用物化运算符(ToList、Count)之前不会运行。这意味着定义查询后修改源会影响结果。使用 .ToList() 强制立即执行并快照数据。
Dim numbers = New List(Of Integer) From {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
' query syntax (SQL-like)
Dim evens = From n In numbers
Where n Mod 2 = 0
Select n
Order By n Descending
' method syntax (fluent)
Dim evens2 = numbers.Where(Function(n) n Mod 2 = 0).
OrderByDescending(Function(n) n)
' both produce the same result
For Each n In evens
Console.WriteLine(n) ' 10, 8, 6, 4, 2
Next
' projection (transform)
Dim squares = numbers.Select(Function(n) n * n)
Dim names = numbers.Select(Function(n) $"Number {n}")
' filtering with multiple conditions
Dim filtered = From n In numbers
Where n > 3 AndAlso n < 8
Select n
' type inference (Dim is required)
Dim result = From p In people
Where p.Age >= 18
Select p.Name, p.Age
Order By Age
' deferred execution (query runs when iterated)
Dim query = numbers.Where(Function(n) n > 5) ' not executed yet
numbers.Add(11) ' query will include 11!
For Each n In query ' executes now
Next聚合与分组
LINQ 聚合运算符(Sum、Average、Min、Max、Count、Any、All)将序列折叠为单个值。GroupBy 按键分区元素;每个组有一个 .Key 且本身可枚举。查询语法 'Group By...Into Group' 是 VB 特有的。每组的多个聚合在报表中很常见。Any/All 短路(提前停止)—— 适用于无需完全迭代即可检查条件。聚合函数在空序列上抛出异常;使用可空变体(Sum 返回 0,Average 抛出)或 DefaultIfEmpty 处理此问题。
Dim people = New List(Of Person) From {
New Person With {.Name = "Al", .Dept = "Eng", .Salary = 90000},
New Person With {.Name = "Bo", .Dept = "Eng", .Salary = 70000},
New Person With {.Name = "Cy", .Dept = "Sales", .Salary = 60000}
}
' aggregation
Dim total = people.Sum(Function(p) p.Salary)
Dim avg = people.Average(Function(p) p.Salary)
Dim max = people.Max(Function(p) p.Salary)
Dim count = people.Count(Function(p) p.Dept = "Eng")
Dim any = people.Any(Function(p) p.Salary > 80000)
Dim all = people.All(Function(p) p.Salary > 0)
' grouping
Dim byDept = From p In people
Group p By p.Dept Into Group
Select Dept, Count = Group.Count(), Avg = Group.Average(Function(x) x.Salary)
For Each g In byDept
Console.WriteLine($"{g.Dept}: {g.Count} people, avg ${g.Avg}")
Next
' method syntax grouping
Dim grouped = people.GroupBy(Function(p) p.Dept)
For Each g In grouped
Console.WriteLine($"{g.Key}: {g.Count()} people")
Next
' group with multiple aggregates
Dim stats = people.GroupBy(Function(p) p.Dept).
Select(Function(g) New With {
.Dept = g.Key,
.Count = g.Count(),
.Total = g.Sum(Function(p) p.Salary),
.Min = g.Min(Function(p) p.Salary)
})连接、Zip 与集合操作
Join 执行内连接(匹配键);Group Join 配合 DefaultIfEmpty 创建左连接。Zip 按位置配对元素。集合操作(Union、Intersect、Except、Distinct)使用默认相等比较器 —— 对于复杂类型,重写 Equals/GetHashCode 或传递自定义 IEqualityComparer。Concat 追加而不移除重复项(不同于 Union)。所有这些都是惰性的,除非物化。对于大型数据集,考虑使用 HashSet 或 Dictionary 进行 O(1) 查找,而非 Join 的 O(n*m) 嵌套循环。
Dim employees = New List(Of Employee) From {
New Employee With {.Id = 1, .Name = "Al", .DeptId = 10},
New Employee With {.Id = 2, .Name = "Bo", .DeptId = 20}
}
Dim departments = New List(Of Department) From {
New Department With {.Id = 10, .Name = "Eng"},
New Department With {.Id = 20, .Name = "Sales"},
New Department With {.Id = 30, .Name = "HR"}
}
' inner join
Dim result = From e In employees
Join d In departments On e.DeptId Equals d.Id
Select e.Name, Dept = d.Name
' group join (left join with grouped results)
Dim leftJoin = From d In departments
Group Join e In employees On d.Id Equals e.DeptId Into Group
From e In Group.DefaultIfEmpty()
Select Dept = d.Name, Emp = If(e Is Nothing, "Vacant", e.Name)
' method syntax join
Dim joined = employees.Join(departments,
Function(e) e.DeptId,
Function(d) d.Id,
Function(e, d) New With { .Name = e.Name, .Dept = d.Name })
' zip (pair elements)
Dim nums = {1, 2, 3}
Dim words = {"one", "two", "three"}
Dim pairs = nums.Zip(words, Function(n, w) $"{n}={w}")
' set operations
Dim a = {1, 2, 3, 4}
Dim b = {3, 4, 5, 6}
Dim union = a.Union(b) ' 1,2,3,4,5,6
Dim intersect = a.Intersect(b) ' 3,4
Dim except = a.Except(b) ' 1,2
Dim concat = a.Concat(b) ' 1,2,3,4,3,4,5,6
Dim distinct = concat.Distinct() ' 1,2,3,4,5,6排序、分页与元素操作
OrderBy/ThenBy 排序(稳定);OrderByDescending/ThenByDescending 降序排序。多个排序键在 OrderBy 之后使用 ThenBy。分页使用 Skip(偏移)和 Take(限制)—— 对 UI 中的大型数据集至关重要。First/Last 在空时抛出;FirstOrDefault 返回默认值(引用类型为 Nothing,数字为 0)。Single 强制恰好一个匹配(否则抛出)—— 适用于验证。TakeWhile/SkipWhile 基于谓词分区直到失败。所有这些都是惰性的,除了 First/Last/Single 会强制迭代。
Dim nums = {5, 2, 8, 1, 9, 3, 7, 4, 6}
' sorting
Dim asc = nums.OrderBy(Function(n) n)
Dim desc = nums.OrderByDescending(Function(n) n)
Dim multi = people.OrderBy(Function(p) p.Dept).
ThenBy(Function(p) p.Name)
Dim multiDesc = people.OrderByDescending(Function(p) p.Dept).
ThenByDescending(Function(p) p.Salary)
' reverse
Dim reversed = nums.Reverse()
' paging (skip + take)
Dim page = nums.Skip(5).Take(3) ' elements 6,7,8 (0-indexed)
Dim firstPage = nums.Take(5) ' first 5
' element operations
Dim first = nums.First() ' throws if empty
Dim firstOr = nums.FirstOrDefault() ' default (0) if empty
Dim last = nums.Last()
Dim single = nums.Single(Function(n) n = 5) ' throws if not exactly one
Dim singleOr = nums.SingleOrDefault(Function(n) n = 99) ' 0 if not found
' element at position
Dim third = nums.ElementAt(2) ' throws if out of range
Dim thirdOr = nums.ElementAtOrDefault(99) ' 0 if out of range
' find index (not LINQ, but related)
Dim idx = Array.FindIndex(nums, Function(n) n > 5)
' partition
Dim large = nums.Where(Function(n) n > 5)
Dim small = nums.TakeWhile(Function(n) n < 8) ' stops at first false
Dim skipSmall = nums.SkipWhile(Function(n) n < 8)自定义 LINQ 与表达式树
扩展方法(用 <Extension()> 修饰)向任何类型添加类似 LINQ 的运算符。自定义聚合(Median、Mode、StandardDeviation)填补内置运算符的空白。IEnumerable(Of T) 使用委托(内存中);IQueryable(Of T) 使用表达式树转换为 SQL(EF/LINQ to SQL)—— 混合它们可能导致客户端评估(慢)。ToList/ToArray/ToDictionary 强制评估并快照结果。PLINQ(.AsParallel)并行化查询 —— 用于大型集合上的 CPU 密集型操作,但注意排序和线程安全。
' custom extension method
Imports System.Runtime.CompilerServices
<Extension()>
Public Function WhereNot(Of T)(source As IEnumerable(Of T),
predicate As Func(Of T, Boolean)) As IEnumerable(Of T)
Return source.Where(Function(x) Not predicate(x))
End Function
' usage
Dim odds = numbers.WhereNot(Function(n) n Mod 2 = 0)
' custom aggregation
<Extension()>
Public Function Median(source As IEnumerable(Of Double)) As Double
Dim sorted = source.OrderBy(Function(x) x).ToList()
Dim count = sorted.Count
If count = 0 Then Return 0
If count Mod 2 = 0 Then
Return (sorted(count 2 - 1) + sorted(count 2)) / 2
Else
Return sorted(count 2)
End If
End Function
' usage
Dim med = numbers.Median()
' IEnumerable vs IQueryable
' IEnumerable: LINQ to Objects (in-memory, delegates)
' IQueryable: LINQ to SQL/EF (translates to SQL, expression trees)
' force evaluation
Dim list = query.ToList() ' List(Of T)
Dim array = query.ToArray() ' T()
Dim dict = query.ToDictionary(Function(x) x.Id)
Dim lookup = query.ToLookup(Function(x) x.Category)
' PLINQ (parallel)
Dim parallel = nums.AsParallel().
Where(Function(n) IsPrime(n)).
Select(Function(n) n * 2).
ToList()多线程与异步
Task 与 Async/Await
Async/Await 是编写异步代码的现代方式 —— 它看起来同步但不阻塞线程。将方法标记为 Async 并在返回 Task 的调用上使用 Await。UI 事件处理器可以是 Async Sub(唯一可接受 Async Sub 的地方)。Task.Run 将 CPU 密集型工作卸载到线程池。Task.WhenAll 等待所有任务(并行);Task.WhenAny 等待第一个完成。编译器生成一个状态机来处理延续、异常传播和上下文捕获。始终优先使用 Async/Await 而非手动线程或回调。
Imports System.Threading.Tasks
' async function
Async Function DownloadAsync(url As String) As Task(Of String)
Using client As New Net.Http.HttpClient()
Return Await client.GetStringAsync(url)
End Function
End Function
' call async from event handler
Private Async Sub btnDownload_Click(sender As Object, e As EventArgs) Handles btnDownload.Click
btnDownload.Enabled = False
Try
Dim content = Await DownloadAsync("https://example.com")
txtResult.Text = content
Catch ex As Exception
MessageBox.Show(ex.Message)
Finally
btnDownload.Enabled = True
End Try
End Sub
' run CPU-bound work on background thread
Async Function ProcessDataAsync(data As Byte()) As Task(Of Result)
Return Await Task.Run(Function()
' CPU-intensive work
Return Analyze(data)
End Function)
End Function
' Task.Run overloads
Dim t1 = Task.Run(Function() Compute(1))
Dim t2 = Task.Run(Async Function() Await FetchAsync())
' wait for multiple tasks
Dim tasks = New List(Of Task) From {t1, t2, t3}
Await Task.WhenAll(tasks)
' wait for any
Dim firstDone = Await Task.WhenAny(t1, t2, t3)
' task continuation
Dim result = Await t1.ContinueWith(Function(t) t.Result * 2)线程安全与同步
SyncLock(VB)/ lock(C#)提供互斥 —— 一次只有一个线程进入块。锁定私有 Object,切勿锁定 Me 或字符串。Interlocked 提供无锁的原子操作(对简单情况更快)。Mutex 跨进程工作(单实例应用)。SemaphoreSlim 限制并发访问(例如,一次最多 3 个 API 调用)。始终在 Finally 块中释放锁/信号量。当两个线程互相等待时发生死锁 —— 以一致的顺序获取锁。使用 ConcurrentDictionary、ConcurrentQueue 进行无锁线程安全集合。
Imports System.Threading
' lock (mutual exclusion)
Private ReadOnly _lock As New Object
Private _counter As Integer = 0
Sub Increment()
SyncLock _lock
_counter += 1
End SyncLock
End Sub
' Monitor (equivalent to SyncLock)
Monitor.Enter(_lock)
Try
_counter += 1
Finally
Monitor.Exit(_lock)
End Try
' Interlocked (atomic operations, no lock needed)
Interlocked.Increment(_counter)
Interlocked.Add(_counter, 10)
Interlocked.Exchange(_counter, 0)
Interlocked.CompareExchange(_counter, 1, 0) ' if 0, set to 1
' Mutex (cross-process)
Dim mutex As New Mutex(False, "GlobalMyAppMutex")
mutex.WaitOne()
Try
' critical section
Finally
mutex.ReleaseMutex()
End Try
' SemaphoreSlim (limit concurrent access)
Dim sem As New SemaphoreSlim(3) ' max 3 concurrent
Await sem.WaitAsync()
Try
' limited concurrency
Finally
sem.Release()
End Try
' CountdownEvent
Dim cde As New CountdownEvent(5)
Parallel.For(0, 5, Sub(i)
' work
cde.Signal()
End Sub)
cde.Wait()并行与 BackgroundWorker
Parallel.For/ForEach 跨线程池线程分区工作 —— 非常适合 CPU 密集型循环。PLINQ(.AsParallel)并行化 LINQ 查询。使用 ParallelOptions.MaxDegreeOfParallelism 限制线程。CancellationToken 启用协作式取消。BackgroundWorker 是旧版但对 WinForms 很方便 —— 它自动将 ProgressChanged 和 RunWorkerCompleted 封送到 UI 线程。对于新代码,优先使用 Task.Run + IProgress(Of T) + Async/Await。并行循环是阻塞的(不同于 Async);不要在 UI 线程上使用它们。
Imports System.Threading.Tasks
' Parallel.For (CPU-bound parallelism)
Parallel.For(0, 1000, Sub(i)
Process(i)
End Sub)
' Parallel.ForEach
Parallel.ForEach(files, Sub(f)
Compress(f)
End Sub)
' with options
Dim opts As New ParallelOptions With {.MaxDegreeOfParallelism = 4}
Parallel.For(0, 1000, opts, Sub(i) Process(i))
' PLINQ
Dim results = numbers.AsParallel().
Where(Function(n) IsPrime(n)).
Select(Function(n) n * 2).
ToArray()
' cancel parallel work
Dim cts As New CancellationTokenSource()
Parallel.For(0, 1000,
New ParallelOptions With {.CancellationToken = cts.Token},
Sub(i, state)
If SomeCondition Then state.Break()
Process(i)
End Sub)
' BackgroundWorker (legacy, but simple for WinForms)
Dim bgw As New BackgroundWorker With {.WorkerReportsProgress = True, .WorkerSupportsCancellation = True}
AddHandler bgw.DoWork, Sub(sender, e)
For i = 1 To 100
If bgw.CancellationPending Then
e.Cancel = True
Return
End If
bgw.ReportProgress(i)
Thread.Sleep(50)
Next
End Sub
AddHandler bgw.ProgressChanged, Sub(sender, e)
ProgressBar1.Value = e.ProgressPercentage
End Sub
AddHandler bgw.RunWorkerCompleted, Sub(sender, e)
MessageBox.Show("Done!")
End Sub
bgw.RunWorkerAsync()计时器与 UI 线程封送
.NET 中有三种计时器:Windows.Forms.Timer(UI 线程,对窗体最简单)、System.Threading.Timer(线程池,轻量)和 System.Timers.Timer(组件,服务器场景)。只有 Forms.Timer 可以直接更新 UI;其他需要 Invoke。Control.Invoke 将委托封送到 UI 线程 —— 先检查 InvokeRequired。ConfigureAwait(False) 提高库代码性能(无上下文捕获)但之后阻止 UI 访问。SynchronizationContext.Post 是封送到特定上下文(UI、ASP.NET)的更通用方式。
' Windows.Forms.Timer (UI thread, simple)
Dim timer1 As New Windows.Forms.Timer With {.Interval = 1000}
AddHandler timer1.Tick, Sub(sender, e)
Label1.Text = Date.Now.ToString()
End Sub
timer1.Start()
' System.Threading.Timer (thread pool, not UI thread)
Dim timer2 As New Threading.Timer(
Sub(state)
' runs on thread pool thread!
' must marshal to UI thread to update controls:
Me.Invoke(Sub() Label1.Text = Date.Now.ToString())
End Sub,
Nothing, 0, 1000)
' System.Timers.Timer (component, fires on thread pool)
Dim timer3 As New Timers.Timer With {.Interval = 1000, .AutoReset = True}
AddHandler timer3.Elapsed, Sub(sender, e)
Me.Invoke(Sub() Label1.Text = Date.Now.ToString())
End Sub
timer3.Start()
' UI thread marshaling
If Label1.InvokeRequired Then
Label1.Invoke(Sub() Label1.Text = "updated")
Else
Label1.Text = "updated"
End If
' async with ConfigureAwait
Await SomeAsync().ConfigureAwait(True) ' capture context (default)
Await SomeAsync().ConfigureAwait(False) ' don't capture (faster, but no UI access)
' SynchronizationContext for custom marshaling
Dim uiContext = SynchronizationContext.Current
uiContext.Post(Sub() Label1.Text = "done", Nothing)并发集合与通道
并发集合(ConcurrentDictionary、Queue、Stack、Bag)是常规集合的线程安全替代品 —— 使用它们而非锁定。ConcurrentDictionary 上的 AddOrUpdate/GetOrAdd 是原子复合操作。BlockingCollection 实现生产者-消费者模式,带阻塞 Add/Take —— 非常适合工作队列。Channels(System.Threading.Channels)是现代的、异步友好的替代品,带背压。对于 UI 线程安全,使用 Invoke/BeginInvoke 而非并发集合。始终优先使用这些内置原语而非手动锁定 —— 它们经过测试和优化。
Imports System.Collections.Concurrent
' ConcurrentDictionary (thread-safe dictionary)
Dim dict As New ConcurrentDictionary(Of String, Integer)
dict.TryAdd("a", 1)
dict.AddOrUpdate("a", 1, Function(key, old) old + 1) ' atomic
Dim val = dict.GetOrAdd("b", Function(key) ComputeValue(key))
' ConcurrentQueue (FIFO, thread-safe)
Dim queue As New ConcurrentQueue(Of String)
queue.Enqueue("first")
Dim item As String
If queue.TryDequeue(item) Then
Console.WriteLine(item)
End If
' ConcurrentStack (LIFO)
Dim stack As New ConcurrentStack(Of Integer)
stack.Push(1)
Dim popped As Integer
stack.TryPop(popped)
' ConcurrentBag (unordered, fast for producer=consumer)
Dim bag As New ConcurrentBag(Of Integer)
bag.Add(42)
' BlockingCollection (producer-consumer pattern)
Dim bc As New BlockingCollection(Of Integer)(boundedCapacity:=100)
' producer
Task.Run(Sub()
For i = 1 To 1000
bc.Add(i) ' blocks if full
Next
bc.CompleteAdding()
End Sub)
' consumer
Task.Run(Sub()
For Each x In bc.GetConsumingEnumerable() ' blocks if empty
Process(x)
Next
End Sub)
' Channels (modern, high-performance)
' NuGet: System.Threading.Channels
Dim channel = System.Threading.Channels.Channel.CreateBounded(Of String)(100)
Await channel.Writer.WriteAsync("message")
Dim msg = Await channel.Reader.ReadAsync()注册表与 Windows API
注册表读写
Windows 注册表存储应用设置和系统配置。使用 Microsoft.Win32.Registry 访问。HKEY_CURRENT_USER(HKCU)是每用户的(无需管理员);HKEY_LOCAL_MACHINE(HKLM)是系统范围的(需要管理员)。读取时始终提供默认值(缺失时返回 Nothing)。使用 RegistryValueKind 指定类型(String、DWord、Binary)。Run 键在登录时自动启动应用。将 RegistryKey 包装在 Using 中以关闭句柄。编辑注册表要谨慎 —— 错误可能破坏 Windows。
Imports Microsoft.Win32
' read a value
Dim value As String = Registry.GetValue(
"HKEY_CURRENT_USERSoftwareMyApp",
"Setting1",
"default") ' default if not found
Console.WriteLine(value)
' write a value
Registry.SetValue(
"HKEY_CURRENT_USERSoftwareMyApp",
"Setting1",
"my value")
' using RegistryKey (more control)
Using key As RegistryKey = Registry.CurrentUser.OpenSubKey("SoftwareMyApp", writable:=True)
If key IsNot Nothing Then
Dim setting = key.GetValue("Setting1")
key.SetValue("Setting2", 42, RegistryValueKind.DWord)
key.DeleteValue("Setting2", throwOnMissingValue:=False)
End If
End Using
' create subkey
Using key As RegistryKey = Registry.CurrentUser.CreateSubKey("SoftwareMyAppSub")
key.SetValue("Name", "Test")
End Using
' enumerate subkeys and values
Using key As RegistryKey = Registry.LocalMachine.OpenSubKey("SOFTWARE")
For Each subkeyName In key.GetSubKeyNames()
Console.WriteLine(subkeyName)
Next
For Each valueName In key.GetValueNames()
Console.WriteLine($"{valueName}: {key.GetValue(valueName)}")
Next
End Using
' run on startup (add to Run key)
Registry.SetValue("HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionRun",
"MyApp", Application.ExecutablePath)P/Invoke:调用 Windows API
P/Invoke(Platform Invoke)让 VB 通过 DllImport 调用非托管 Windows API 函数。声明匹配 C API 的函数签名;运行时自动封送类型(String ↔ LPSTR,Integer ↔ DWORD)。对输出参数使用 ByRef,对按引用传递的结构使用 StructLayout。CharSet.Auto 根据操作系统选择 ANSI 或 Unicode。常用库:user32(窗口、消息)、kernel32(系统、文件)、gdi32(图形)。始终将 P/Invoke 包装在 NativeMethods 类中。Pinvoke.net 是签名的好资源。
Imports System.Runtime.InteropServices
Public Class NativeMethods
' MessageBox from user32.dll
<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Public Shared Function MessageBox(hWnd As IntPtr, text As String,
caption As String, type As Integer) As Integer
End Function
' GetSystemMetrics
<DllImport("user32.dll")>
Public Shared Function GetSystemMetrics(nIndex As Integer) As Integer
End Function
' Beep
<DllImport("kernel32.dll")>
Public Shared Function Beep(frequency As Integer, duration As Integer) As Boolean
End Function
' SendMessage (for custom control messages)
<DllImport("user32.dll", CharSet:=CharSet.Auto)>
Public Shared Function SendMessage(hWnd As IntPtr, msg As Integer,
wParam As IntPtr, lParam As IntPtr) As IntPtr
End Function
' structures
<StructLayout(LayoutKind.Sequential)>
Public Structure POINT
Public X As Integer
Public Y As Integer
End Structure
<DllImport("user32.dll")>
Public Shared Function GetCursorPos(ByRef lpPoint As POINT) As Boolean
End Function
End Class
' usage
NativeMethods.MessageBox(IntPtr.Zero, "Hello from API!", "Test", 0)
NativeMethods.Beep(440, 500) ' 440 Hz for 500ms
Dim p As NativeMethods.POINT
NativeMethods.GetCursorPos(p)
Console.WriteLine($"Cursor at {p.X}, {p.Y}")
' screen dimensions
Dim width = NativeMethods.GetSystemMetrics(0) ' SM_CXSCREEN
Dim height = NativeMethods.GetSystemMetrics(1) ' SM_CYSCREEN进程与 Shell 操作
Process.Start 启动外部程序。UseShellExecute=False 配合 RedirectStandardOutput 让你以编程方式捕获输出 —— 对命令行工具至关重要。始终 WaitForExit 并读取输出以避免死锁(输出缓冲区可能填满并阻塞进程)。GetProcesses 枚举运行中的进程;GetProcessesByName 查找特定的进程。Verb='runas' 提升为管理员(触发 UAC)。对于长时间运行的进程,订阅 Exited 事件或使用异步模式。对用户提供的文件路径要谨慎 —— 验证以防止命令注入。
Imports System.Diagnostics
' start a process
Process.Start("notepad.exe")
Process.Start("notepad.exe", "file.txt")
Process.Start("https://example.com") ' opens default browser
Process.Start("mailto:[email protected]")
' with ProcessStartInfo (more control)
Dim psi As New ProcessStartInfo With {
.FileName = "cmd.exe",
.Arguments = "/c dir",
.UseShellExecute = False,
.RedirectStandardOutput = True,
.CreateNoWindow = True
}
Using p As Process = Process.Start(psi)
Dim output = p.StandardOutput.ReadToEnd()
p.WaitForExit()
Console.WriteLine(output)
End Using
' async read output
Using p As Process = Process.Start(psi)
Dim output = Await p.StandardOutput.ReadToEndAsync()
Await Task.Run(Sub() p.WaitForExit())
End Using
' get running processes
For Each proc In Process.GetProcesses()
Console.WriteLine($"{proc.ProcessName} (PID {proc.Id})")
Next
' find specific process
Dim excel = Process.GetProcessesByName("EXCEL").FirstOrDefault()
If excel IsNot Nothing Then
Console.WriteLine($"Excel memory: {excel.WorkingSet64 1024} KB")
End If
' run as admin
psi.Verb = "runas" ' triggers UAC prompt
Process.Start(psi)环境与系统信息
Environment 提供系统和用户信息。GetFolderPath 配合 SpecialFolder 是查找标准目录(AppData、MyDocuments、Temp)的正确方式 —— 切勿硬编码路径。环境变量跨机器配置行为。GetCommandLineArgs 包括可执行文件作为第一个元素。对于详细的硬件信息,使用 WMI(System.Management)—— 查询 Win32_Processor、Win32_LogicalDisk 等类。Screen.AllScreens(Windows Forms)为多显示器设置提供监视器信息。始终使用这些 API 而非猜测路径或设置。
' environment variables
Dim path = Environment.GetEnvironmentVariable("PATH")
Environment.SetEnvironmentVariable("MY_VAR", "value")
Dim allVars = Environment.GetEnvironmentVariables()
' special folders
Dim appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
Dim myDocs = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
Dim temp = Environment.GetFolderPath(Environment.SpecialFolder.Temp)
Dim programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
' system info
Console.WriteLine($"Machine: {Environment.MachineName}")
Console.WriteLine($"User: {Environment.UserName}")
Console.WriteLine($"OS: {Environment.OSVersion}")
Console.WriteLine($"64-bit: {Environment.Is64BitOperatingSystem}")
Console.WriteLine($"Processors: {Environment.ProcessorCount}")
Console.WriteLine($".NET: {Environment.Version}")
Console.WriteLine($"Uptime: {Environment.TickCount64 1000} seconds")
' command line args
Dim args = Environment.GetCommandLineArgs()
' args(0) is the executable path
' current directory
Environment.CurrentDirectory = "C: emp"
' system power
Console.WriteLine($"System uptime: {TimeSpan.FromMilliseconds(Environment.TickCount64)}")
' WMI for detailed hardware info
Imports System.Management
Dim searcher As New ManagementObjectSearcher("SELECT * FROM Win32_Processor")
For Each obj In searcher.Get()
Console.WriteLine($"CPU: {obj("Name")}")
Next
' screen info
Dim screens = Screen.AllScreens
For Each s In screens
Console.WriteLine($"{s.DeviceName}: {s.Bounds.Width}x{s.Bounds.Height}")
NextINI 文件与配置
INI 文件是旧版但仍用于简单配置。Windows API 函数(WritePrivateProfileString、GetPrivateProfileString)处理解析。对于现代 .NET,使用 app.config(XML)或 appsettings.json(JSON)配合 ConfigurationManager 或 Microsoft.Extensions.Configuration。JSON 配置支持嵌套结构、数组和环境特定覆盖(appsettings.Production.json)。配置构建器可以组合多个源(文件、环境变量、命令行)。始终将配置与代码分离以实现部署灵活性。
' INI files (legacy but still common)
Imports System.Runtime.InteropServices
Public Class IniFile
Private path As String
<DllImport("kernel32.dll", CharSet:=CharSet.Unicode)>
Private Shared Function WritePrivateProfileString(section As String,
key As String, value As String, filePath As String) As Boolean
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Unicode)>
Private Shared Function GetPrivateProfileString(section As String,
key As String, defaultVal As String,
<Out> ByVal retVal As StringBuilder, size As Integer,
filePath As String) As Integer
End Function
Public Sub New(filePath As String)
path = filePath
End Sub
Public Function Read(section As String, key As String, Optional defaultVal As String = "") As String
Dim sb As New StringBuilder(255)
GetPrivateProfileString(section, key, defaultVal, sb, 255, path)
Return sb.ToString()
End Function
Public Sub Write(section As String, key As String, value As String)
WritePrivateProfileString(section, key, value, path)
End Sub
End Class
' usage
Dim ini As New IniFile("config.ini")
ini.Write("Settings", "Theme", "Dark")
Dim theme = ini.Read("Settings", "Theme", "Light")
' modern: app.config / appsettings.json
' app.config:
' <appSettings>
' <add key="Theme" value="Dark" />
' </appSettings>
Dim theme2 = Configuration.ConfigurationManager.AppSettings("Theme")
' JSON config (with System.Configuration or Microsoft.Extensions.Configuration)
' appsettings.json:
' { "Theme": "Dark", "MaxItems": 100 }集合与泛型深入
List、Dictionary 与 HashSet
List(Of T) 是主力 —— 动态数组,O(1) 追加,O(n) 插入/删除。Dictionary(Of K,V) 提供 O(1) 键查找(哈希表)。HashSet(Of T) 存储唯一元素,快速集合操作(Union、Intersect、Except)。SortedDictionary 保持键排序(二叉搜索树)。LinkedList 是双向链表(任意位置快速插入/删除,但无索引访问)。根据访问模式选择:List 用于索引,Dictionary 用于键,HashSet 用于成员资格,SortedDictionary 用于有序迭代。所有都是泛型的(类型安全,无装箱)。
Imports System.Collections.Generic
' List(Of T)
Dim list As New List(Of String) From {"a", "b", "c"}
list.Add("d")
list.AddRange({"e", "f"})
list.Insert(0, "first")
list.Remove("b")
list.RemoveAt(0)
list.Sort()
list.Reverse()
Dim count = list.Count
Dim hasA = list.Contains("a")
Dim idx = list.IndexOf("c")
Dim slice = list.GetRange(0, 2) ' first 2 elements
Dim arr = list.ToArray()
' Dictionary(Of TKey, TValue)
Dim dict As New Dictionary(Of String, Integer)
dict.Add("one", 1)
dict("two") = 2
dict.TryGetValue("one", Dim val As Integer) ' val = 1, returns True
For Each kvp In dict
Console.WriteLine($"{kvp.Key} = {kvp.Value}")
Next
Dim keys = dict.Keys.ToList()
Dim vals = dict.Values.ToList()
' HashSet(Of T) (unique elements, O(1) lookup)
Dim set1 As New HashSet(Of Integer) From {1, 2, 3}
Dim set2 As New HashSet(Of Integer) From {3, 4, 5}
set1.Add(3) ' no effect (already exists)
Dim union = New HashSet(Of Integer)(set1) : union.UnionWith(set2) ' 1,2,3,4,5
Dim inter = New HashSet(Of Integer)(set1) : inter.IntersectWith(set2) ' 3
Dim diff = New HashSet(Of Integer)(set1) : diff.ExceptWith(set2) ' 1,2
' SortedDictionary / SortedList (sorted by key)
Dim sorted As New SortedDictionary(Of String, Integer)
sorted("zebra") = 1
sorted("apple") = 2 ' iterates in sorted order
' LinkedList (fast insert/remove at ends)
Dim ll As New LinkedList(Of Integer)
ll.AddFirst(1)
ll.AddLast(2)Queue、Stack 与 ObservableCollection
Queue(FIFO)和 Stack(LIFO)专用于有序访问模式。ObservableCollection 在添加/删除/替换项时引发 CollectionChanged 事件 —— 对 MVVM 数据绑定至关重要。BindingList 扩展了这一点,具有编辑功能(AddingNew、AllowEdit、AllowRemove)。ReadOnlyCollection 包装列表以防止修改(返回原始列表,而非副本)。KeyedCollection 结合列表和字典语义(按索引或键访问)。对于线程安全版本,使用 System.Collections.Concurrent 中的 ConcurrentQueue、ConcurrentStack、ConcurrentDictionary。
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
' Queue(Of T) (FIFO)
Dim queue As New Queue(Of String)
queue.Enqueue("first")
queue.Enqueue("second")
Dim next1 = queue.Dequeue() ' "first"
Dim peek = queue.Peek() ' "second" (doesn't remove)
Dim hasItems = queue.Count > 0
' Stack(Of T) (LIFO)
Dim stack As New Stack(Of Integer)
stack.Push(1)
stack.Push(2)
stack.Push(3)
Dim top = stack.Pop() ' 3
Dim peek = stack.Peek() ' 2
' ObservableCollection (notifies on changes - for data binding)
Dim people As New ObservableCollection(Of Person)()
AddHandler people.CollectionChanged, Sub(sender, e)
If e.Action = NotifyCollectionChangedAction.Add Then
Console.WriteLine($"Added: {CType(e.NewItems(0), Person).Name}")
End If
End Sub
people.Add(New Person With {.Name = "Alice"}) ' triggers event
' BindingList(Of T) (editable, for DataGridView)
Dim list As New BindingList(Of Person)()
list.AddingNew = Function() New Person()
list.RaiseListChangedEvents = True
' ReadOnlyCollection (wrapper, prevents modification)
Dim ro As New ReadOnlyCollection(Of String)(list)
' ro.Add("x") ' throws NotSupportedException
' KeyedCollection (abstract, dictionary-like by key)
Public Class PersonCollection
Inherits KeyedCollection(Of String, Person)
Protected Overrides Function GetKeyForItem(item As Person) As String
Return item.Name
End Function
End Class泛型方法与约束
泛型提供类型安全而无装箱/拆箱开销。约束(Of T As ...)限制类型参数:Class(引用类型)、Structure(值类型)、New(无参构造函数)、IComparable(接口)或基类。多个约束使用花括号:Of T As {Class, New, IComparable(Of T)}。泛型方法从参数推断类型。泛型在 .NET 中是具体化的(运行时可用类型信息,不同于 Java 的类型擦除)。对集合、算法和工具类使用泛型以避免代码重复同时保持类型安全。
' generic method
Public Function Max(Of T As IComparable(Of T))(a As T, b As T) As T
If a.CompareTo(b) > 0 Then Return a
Return b
End Function
Dim m1 = Max(3, 5) ' Integer
Dim m2 = Max("apple", "banana") ' String
Dim m3 = Max(3.14, 2.71) ' Double
' multiple type parameters
Public Function Pair(Of T1, T2)(first As T1, second As T2) As Tuple(Of T1, T2)
Return Tuple.Create(first, second)
End Function
' constraints
Public Class Repository(Of T As Class, New)
' T must be a reference type with parameterless constructor
Public Function Create() As T
Return New T()
End Function
End Class
' constraint types:
' Class - reference type
' Structure - value type
' New - has parameterless constructor
' IComparable(Of T) - implements interface
' BaseClass - inherits from base class
' generic method with interface constraint
Public Sub Sort(Of T As IComparable(Of T))(ByRef arr As T())
Array.Sort(arr)
End Sub
' multiple constraints
Public Function Process(Of T As {Class, IComparable(Of T), New})(items As List(Of T)) As T
Dim item As New T()
items.Sort()
Return items(0)
End Function
' generic class with method
Public Class Cache(Of TKey, TValue)
Private dict As New Dictionary(Of TKey, TValue)
Public Sub Add(key As TKey, value As TValue)
dict.Add(key, value)
End Sub
Public Function Get(key As TKey) As TValue
Return dict(key)
End Function
End Class自定义集合与 IEnumerable
实现 IEnumerable(Of T) 启用 For Each 迭代。Iterator/Yield 模式(VB 14+)为惰性求值生成状态机 —— 值按需产生。这对大型或无限序列节省内存。CollectionBase 是旧版基类;对于新代码,优先实现 ICollection(Of T) 或继承 List(Of T)。当你需要特殊行为(验证、通知、延迟加载)时,自定义集合才有意义。对于大多数情况,组合现有集合而非从头构建。
Imports System.Collections
Imports System.Collections.Generic
' implement IEnumerable (foreach support)
Public Class NumberSequence
Implements IEnumerable(Of Integer)
Private start, count As Integer
Public Sub New(start As Integer, count As Integer)
Me.start = start
Me.count = count
End Sub
Public Function GetEnumerator() As IEnumerator(Of Integer) _
Implements IEnumerable(Of Integer).GetEnumerator
For i = 0 To count - 1
Yield start + i
Next
End Function
Private Function GetEnumerator1() As IEnumerator _
Implements IEnumerable.GetEnumerator
Return GetEnumerator()
End Function
End Class
' usage
Dim seq As New NumberSequence(10, 5)
For Each n In seq
Console.WriteLine(n) ' 10, 11, 12, 13, 14
Next
' iterator with Yield (lazy evaluation)
Public Iterator Function Evens(max As Integer) As IEnumerable(Of Integer)
For i = 0 To max
If i Mod 2 = 0 Then Yield i
Next
End Function
' custom collection class
Public Class PersonList
Inherits CollectionBase
Default Public Property Item(index As Integer) As Person
Get
Return CType(List(index), Person)
End Get
Set(value As Person)
List(index) = value
End Set
End Property
Public Function Add(p As Person) As Integer
Return List.Add(p)
End Function
End Class
' implement ICollection(Of T) for full collection semantics
Public Class CircularBuffer(Of T)
Implements ICollection(Of T)
' ... implement all members
End Class元组与 ValueTuple
ValueTuple(VB 15+)是返回多个值的现代方式 —— 轻量级(值类型),带命名字段以提高可读性。Tuple(System.Tuple)是引用类型,仅有 Item1/Item2 名称。ValueTuple 支持解构(Dim (a, b) = ...)和逐元素相等。对内部方法返回和中间 LINQ 结果使用 ValueTuple。对于公共 API,优先使用具有有意义名称的记录或类。丢弃模式(_)忽略不需要的值。ValueTuple 在 LINQ 中特别有用,用于需要从方法返回的匿名投影。
' Tuple (reference type, up to 8 items)
Dim t1 As New Tuple(Of Integer, String, Boolean)(1, "hello", True)
Console.WriteLine(t1.Item1) ' 1
Console.WriteLine(t1.Item2) ' "hello"
' ValueTuple (value type, lightweight, named fields)
Dim vt1 As (Id As Integer, Name As String, Active As Boolean) =
(1, "hello", True)
Console.WriteLine(vt1.Id) ' 1
Console.WriteLine(vt1.Name) ' "hello"
' without names (Item1, Item2, ...)
Dim vt2 = (1, "hello", True)
Console.WriteLine(vt2.Item1)
' return multiple values
Public Function FindMinMax(numbers As Integer()) As (Min As Integer, Max As Integer)
Return (numbers.Min(), numbers.Max())
End Function
Dim result = FindMinMax({3, 1, 4, 1, 5, 9})
Console.WriteLine($"Min: {result.Min}, Max: {result.Max}")
' deconstruction
Dim (id, name, active) = GetRecord()
Dim (min, max) = FindMinMax({1, 2, 3})
' tuple in LINQ
Dim stats = From p In people
Group By p.Dept Into Group
Select Dept,
Count = Group.Count(),
AvgSalary = Group.Average(Function(x) x.Salary)
' tuple comparison
Dim a = (1, 2)
Dim b = (1, 2)
Console.WriteLine(a = b) ' True (element-wise comparison)
' discard
Dim (first, _, last) = GetThreeValues() ' ignore middle调试与诊断
Debug 与 Trace
Debug 类在 Release 构建中被编译掉(使用 ConditionalAttribute);Trace 在所有构建中都有效。TraceListeners 将输出定向到文件、控制台或事件日志。TraceSwitch(通过 app.config 可配置)在运行时控制详细程度而无需重新编译。TraceSource 提供命名的、细粒度跟踪,带事件类型(Error、Warning、Info、Verbose)。始终使用 Trace 进行生产诊断,使用 Debug 进行开发断言。Conditional 特性在 Release 构建中完全移除方法调用 —— 零开销。在 app.config 中配置监听器以实现部署灵活性。
Imports System.Diagnostics
' Debug class (Debug builds only)
Debug.WriteLine("Debug message")
Debug.Assert(balance >= 0, "Balance is negative!")
Debug.WriteLineIf(verbose, "Verbose output")
Debug.Indent()
Debug.WriteLine("Indented")
Debug.Unindent()
' Trace class (Release builds too)
Trace.WriteLine("Trace message")
Trace.TraceInformation("Info: {0} at {1}", value, Date.Now)
Trace.TraceWarning("Warning: low memory")
Trace.TraceError("Error: connection failed")
' trace listeners
Trace.Listeners.Add(New TextWriterTraceListener("trace.log"))
Trace.Listeners.Add(New ConsoleTraceListener())
Trace.AutoFlush = True
' conditional tracing
#If DEBUG Then
Debug.WriteLine("Only in debug")
#End If
<Conditional("DEBUG")>
Public Sub DebugLog(msg As String)
Console.WriteLine(msg)
End Sub
' TraceSwitch (configurable level)
Dim ts As New TraceSwitch("MySwitch", "Application tracing")
' app.config:
' <switches><add name="MySwitch" value="3" /></switches>
' 0=Off, 1=Error, 2=Warning, 3=Info, 4=Verbose
If ts.TraceInfo Then Trace.WriteLine("Info message")
If ts.TraceVerbose Then Trace.WriteLine("Verbose message")
' TraceSource (named, granular)
Dim ts2 As New TraceSource("MyApp")
ts2.TraceEvent(TraceEventType.Error, 0, "Something failed")
ts2.Switch.Level = SourceLevels.All调试器与断点
调试器特性自定义对象在调试器中的显示方式。DebuggerDisplay 控制摘要字符串;DebuggerBrowsable 控制成员可见性(RootHidden 展平集合)。DebuggerStepThrough/Hidden 控制单步执行行为。条件断点仅在条件为真时暂停 —— 对在大型循环中查找 bug 至关重要。跟踪点在不修改代码的情况下记录消息。即时窗口在运行时评估表达式。编辑并继续让你无需重启即可修复代码。掌握这些调试器功能可显著加快调试速度。
Imports System.Diagnostics
' break into debugger
Debugger.Break() ' if attached, pauses; otherwise prompts
' check if debugger attached
If Debugger.IsAttached Then
Console.WriteLine("Running in debugger")
End If
' launch debugger
Debugger.Launch()
' DebuggerDisplay attribute (custom display in watch window)
<DebuggerDisplay("Name = {Name}, Age = {Age}")>
Public Class Person
Public Property Name As String
Public Property Age As Integer
<DebuggerBrowsable(DebuggerBrowsableState.RootHidden)>
Public Property Items As Integer() = {1, 2, 3}
End Class
' DebuggerStepThrough (skip in F11 step-into)
<DebuggerStepThrough>
Public Sub TrivialMethod()
' debugger won't step into this
End Sub
' DebuggerHidden (not visible in debugger)
<DebuggerHidden>
Public Sub InternalHelper()
End Sub
' conditional breakpoints in IDE:
' Right-click breakpoint > Settings > Condition
' e.g., i == 42 or str.Contains("error")
' tracepoints (log message without code):
' Right-click > Actions > Log message
' "i = {i}, value = {value}"
' edit and continue:
' modify code while paused, continue (limited support)
' immediate window (while debugging):
' ? variableName
' ? methodCall()
' variableName = newValue异常处理策略
在通用异常之前捕获特定异常(最特定的优先)。使用 Throw(而非 Throw ex)保留原始堆栈跟踪。异常过滤器(When 子句)添加条件而不捕获 —— 如果过滤器为 false,异常传播。自定义异常应继承自 Exception(而非已弃用的 ApplicationException),可序列化,并实现三个构造函数。将低级异常包装在领域特定的异常中以抽象实现细节。切勿捕获并静默吞掉异常 —— 至少记录它们。使用全局异常处理器(AppDomain.UnhandledException、Application.ThreadException)作为安全网。
' try/catch/finally
Try
RiskyOperation()
Catch ex As FileNotFoundException
' specific exception first
Logger.Error(ex, "File not found")
Throw New UserFriendlyException("Please check the file path", ex)
Catch ex As IOException
Logger.Error(ex, "IO error")
Throw
Catch ex As Exception
' generic last resort
Logger.Error(ex, "Unexpected error")
Throw
Finally
' always runs (cleanup)
resource?.Dispose()
End Try
' exception filters (VB 14+)
Catch ex As HttpException When ex.StatusCode = 404
' only catch 404s
End Try
' custom exception
<Serializable>
Public Class BusinessRuleException
Inherits Exception
Public Property RuleName As String
Public Sub New(message As String, ruleName As String)
MyBase.New(message)
RuleName = ruleName
End Sub
Public Sub New(message As String, ruleName As String, inner As Exception)
MyBase.New(message, inner)
RuleName = ruleName
End Sub
Protected Sub New(info As Runtime.Serialization.SerializationInfo,
context As Runtime.Serialization.StreamingContext)
MyBase.New(info, context)
RuleName = info.GetString("RuleName")
End Sub
Public Overrides Sub GetObjectData(info As Runtime.Serialization.SerializationInfo,
context As Runtime.Serialization.StreamingContext)
MyBase.GetObjectData(info, context)
info.AddValue("RuleName", RuleName)
End Sub
End Class日志与事件日志
Windows 事件日志非常适合系统级事件(服务启动/停止、严重错误)—— 创建源需要管理员权限。对于应用日志,使用结构化日志框架:Serilog(现代、JSON 友好)、NLog 或 log4net。结构化日志(logger.Information("User {UserId}...", id))保留数据类型以供查询,不同于字符串连接。日志级别(Debug、Info、Warning、Error、Fatal)让你在运行时过滤。始终在日志中包含上下文(用户 ID、订单 ID)以便调试。性能计数器在生产中监视 CPU、内存和自定义指标。
Imports System.Diagnostics
' write to Windows Event Log
If Not EventLog.SourceExists("MyApp") Then
EventLog.CreateEventSource("MyApp", "Application")
End If
EventLog.WriteEntry("MyApp", "Application started", EventLogEntryType.Information)
EventLog.WriteEntry("MyApp", "Disk full!", EventLogEntryType.Error, 1001)
EventLog.WriteEntry("MyApp", "Retrying operation", EventLogEntryType.Warning, 2001)
' read from Event Log
Dim log As New EventLog("Application")
For Each entry In log.Entries.Cast(Of EventLogEntry)().
Where(Function(e) e.Source = "MyApp").
Take(10)
Console.WriteLine($"{entry.TimeGenerated}: {entry.Message}")
Next
' structured logging with Serilog (NuGet)
' Install-Package Serilog.Sinks.File
' Dim logger = New LoggerConfiguration().
' WriteTo.File("log.txt", rollingInterval:=RollingInterval.Day).
' CreateLogger()
' logger.Information("User {UserId} logged in", userId)
' logger.Error(ex, "Failed to process {OrderId}", orderId)
' log4net (alternative)
' <log4net>
' <appender name="FileAppender" type="log4net.Appender.FileAppender">
' <file value="log.txt" />
' </appender>
' </log4net>
' NLog (alternative)
' Dim logger = LogManager.GetCurrentClassLogger()
' logger.Info("Processing started")
' logger.Error(ex, "Processing failed")
' performance counters
Dim pc As New PerformanceCounter("Processor", "% Processor Time", "_Total")
Dim cpuUsage = pc.NextValue()
Thread.Sleep(1000)
cpuUsage = pc.NextValue() ' actual value (needs two samples)单元测试与 TDD
单元测试隔离验证单个方法。MSTest(内置)、xUnit 和 NUnit 是主要框架。TestInitialize/TestCleanup 在每个测试前后运行。Assert 方法验证结果(AreEqual、IsTrue、ThrowsException)。DataTestMethod/DataRow 用多个输入参数化测试。ExpectedException(或 Try/Catch/Assert.Fail)测试异常情况。使用 Moq 模拟依赖项(接口)—— 设置预期行为并验证调用。TDD(测试驱动开发)先写测试,再写代码。目标是业务逻辑的高覆盖率;跳过简单的属性 getter/setter。在 CI/CD 中运行测试以捕获回归。
' MSTest (built-in)
Imports Microsoft.VisualStudio.TestTools.UnitTesting
<TestClass>
Public Class CalculatorTests
Private calc As Calculator
<TestInitialize>
Public Sub Setup()
calc = New Calculator()
End Sub
<TestCleanup>
Public Sub Cleanup()
' cleanup after each test
End Sub
<TestMethod>
Public Sub Add_TwoNumbers_ReturnsSum()
Dim result = calc.Add(2, 3)
Assert.AreEqual(5, result)
End Sub
<TestMethod>
Public Sub Divide_ByZero_ThrowsException()
Try
calc.Divide(10, 0)
Assert.Fail("Should have thrown")
Catch ex As DivideByZeroException
' expected
End Try
End Sub
<TestMethod, ExpectedException(GetType(DivideByZeroException))>
Public Sub Divide_ByZero_Throws()
calc.Divide(10, 0)
End Sub
<DataTestMethod>
<DataRow(1, 2, 3)>
<DataRow(10, 20, 30)>
<DataRow(-1, 1, 0)>
Public Sub Add_VariousInputs(a As Integer, b As Integer, expected As Integer)
Assert.AreEqual(expected, calc.Add(a, b))
End Sub
End Class
' xUnit (alternative)
' <Fact> instead of <TestMethod>
' <Theory> with <InlineData> instead of <DataTestMethod>/<DataRow>
' Moq for mocking
' Dim mockRepo = New Mock(Of IUserRepository)()
' mockRepo.Setup(Function(r) r.GetById(1)).Returns(New User With {.Name = "Test"})
' Dim service = New UserService(mockRepo.Object)集合与字典
ArrayList 与 List(Of T)
List(Of T) 是 ArrayList 的现代、类型安全替代品。始终优先使用它 —— 你获得编译时类型检查和更好的性能(值类型无装箱)。From 子句内联初始化列表。FindAll/Find 使用谓词(lambda)。AddRange 一次追加多个项。
' ArrayList (loosely typed, legacy)
Dim arr As New ArrayList()
arr.Add("hello")
arr.Add(42)
arr.Add(3.14)
arr.Remove("hello")
arr.RemoveAt(0)
For Each item In arr
Console.WriteLine(item)
Next
' List(Of T) — strongly typed, preferred
Dim nums As New List(Of Integer) From {1, 2, 3, 4, 5}
nums.Add(6)
nums.AddRange({7, 8, 9})
nums.Remove(3)
nums.RemoveAt(0)
nums.Sort()
nums.Reverse()
' find and convert
Dim evens = nums.FindAll(Function(n) n Mod 2 = 0)
Dim firstEven = nums.Find(Function(n) n Mod 2 = 0)
Dim arr2 = nums.ToArray()
' index
Dim idx = nums.IndexOf(5)
Console.WriteLine($"Count: {nums.Count}, Contains 5: {nums.Contains(5)}")Dictionary
Dictionary(Of TKey, TValue) 是标准哈希映射。使用 TryGetValue 安全获取值(缺失时返回 False,无异常)。键存在时 Add 抛出异常;索引器(dict(key))静默覆盖。Keys/Values 集合让你迭代或提取。LINQ 适用于字典 —— 它们是 IEnumerable(Of KeyValuePair)。
' Dictionary(Of TKey, TValue)
Dim ages As New Dictionary(Of String, Integer) From {
{"Alice", 30},
{"Bob", 25}
}
' add and update
ages("Carol") = 42 ' add or update
ages.Add("Dave", 35) ' throws if key exists
ages.Remove("Bob")
' access
If ages.ContainsKey("Alice") Then
Console.WriteLine(ages("Alice"))
End If
' safe access
Dim age As Integer
If ages.TryGetValue("Eve", age) Then
Console.WriteLine($"Eve is {age}")
End If
' iterate
For Each kvp In ages
Console.WriteLine($"{kvp.Key}: {kvp.Value}")
Next
' keys and values
Dim names = ages.Keys.ToList()
Dim allAges = ages.Values.ToList()
' LINQ on dictionaries
Dim adults = ages.Where(Function(kvp) kvp.Value >= 30).ToDictionary(Function(kvp) kvp.Key, Function(kvp) kvp.Value)HashSet 与 SortedSet
HashSet 用于快速成员资格测试和集合操作(union、intersect、except)。SortedSet 保持元素排序顺序(二叉搜索树)。两者都有 O(1) 或 O(log n) 操作 —— 比 List.Contains(O(n))快得多。当顺序不重要时使用 HashSet;当需要有序迭代时使用 SortedSet。
' HashSet(Of T) — unique elements, fast lookup
Dim set1 As New HashSet(Of Integer) From {1, 2, 3, 4, 5}
Dim set2 As New HashSet(Of Integer) From {3, 4, 5, 6, 7}
set1.Add(6) ' adds only if not present
set1.Remove(1)
' set operations
Dim union = New HashSet(Of Integer)(set1)
union.UnionWith(set2) ' {1,2,3,4,5,6,7}
Dim intersect = New HashSet(Of Integer)(set1)
intersect.IntersectWith(set2) ' {3,4,5,6}
Dim diff = New HashSet(Of Integer)(set1)
diff.ExceptWith(set2) ' {1,2}
Dim symDiff = New HashSet(Of Integer)(set1)
symDiff.SymmetricExceptWith(set2) ' {1,2,7}
' checks
set1.IsSubsetOf(set2)
set1.IsSupersetOf(set2)
set1.Overlaps(set2) ' any common elements
' SortedSet keeps elements sorted
Dim sorted As New SortedSet(Of Integer) From {5, 2, 8, 1, 9}
' {1, 2, 5, 8, 9}
Console.WriteLine(sorted.Min)
Console.WriteLine(sorted.Max)Queue 与 Stack
Queue 是 FIFO(先进先出)—— 用于任务调度、BFS。Stack 是 LIFO(后进先出)—— 用于撤销、表达式求值、DFS。两者都有 O(1) 入队/出队和压栈/弹栈。从数组构造 Stack 可在一行中反转它。Peek 查看下一个元素而不移除它。
' Queue(Of T) — FIFO
Dim queue As New Queue(Of String)()
queue.Enqueue("first")
queue.Enqueue("second")
queue.Enqueue("third")
Dim next1 = queue.Dequeue() ' "first"
Dim peek = queue.Peek() ' "second" (doesn't remove)
Console.WriteLine($"Count: {queue.Count}")
' process queue
Do While queue.Count > 0
Dim item = queue.Dequeue()
Console.WriteLine(item)
Loop
' Stack(Of T) — LIFO
Dim stack As New Stack(Of Integer)()
stack.Push(1)
stack.Push(2)
stack.Push(3)
Dim top = stack.Pop() ' 3
Dim peek = stack.Peek() ' 2
' reverse using stack
Dim arr = {1, 2, 3, 4, 5}
Dim revStack As New Stack(Of Integer)(arr)
Dim reversed = revStack.ToArray() ' {5, 4, 3, 2, 1}并发集合
并发集合是线程安全的 —— 使用它们而非锁定常规集合。ConcurrentDictionary 的 AddOrUpdate 和 GetOrAdd 是原子的。ConcurrentQueue/Stack 是无锁的。当每个线程主要生产和消费自己的项时,ConcurrentBag 最快。BlockingCollection 是生产者-消费者队列的标准模式。
Imports System.Collections.Concurrent
' thread-safe collections for parallel programming
' ConcurrentDictionary
Dim cd As New ConcurrentDictionary(Of String, Integer)()
cd("a") = 1
cd.AddOrUpdate("a", 1, Function(key, oldVal) oldVal + 1)
cd.GetOrAdd("b", Function(key) 42)
' ConcurrentQueue (lock-free FIFO)
Dim cq As New ConcurrentQueue(Of Integer)()
cq.Enqueue(1)
Dim val As Integer
If cq.TryDequeue(val) Then
Console.WriteLine(val)
End If
' ConcurrentStack (lock-free LIFO)
Dim cs As New ConcurrentStack(Of Integer)()
cs.Push(1)
cs.TryPop(val)
' ConcurrentBag (unordered, fast for same-thread adds)
Dim cb As New ConcurrentBag(Of Integer)()
cb.Add(1)
cb.TryTake(val)
' BlockingCollection (producer-consumer)
Dim bc As New BlockingCollection(Of Integer)(100) ' bounded
' Producer:
Task.Run(Sub()
For i = 1 To 100
bc.Add(i)
Next
bc.CompleteAdding()
End Sub)
' Consumer:
Task.Run(Sub()
For Each item In bc.GetConsumingEnumerable()
Console.WriteLine(item)
Next
End Sub)文件 I/O 高级
流读写
始终将流包装在 Using 块中以确保处置(即使在异常时关闭文件)。File.ReadAllText/ReadAllLines 对小文件很方便。对于大文件,使用 StreamReader 逐行读取。异步 I/O(ReadToEndAsync)保持 UI 响应 —— 切勿在 UI 线程上调用同步文件 I/O。
Imports System.IO
' StreamReader
Using reader As New StreamReader("input.txt")
Dim line As String
Do While reader.Peek() >= 0
line = reader.ReadLine()
Console.WriteLine(line)
Loop
End Using
' read all at once
Dim allText = File.ReadAllText("input.txt")
Dim allLines = File.ReadAllLines("input.txt")
' StreamWriter
Using writer As New StreamWriter("output.txt")
writer.WriteLine("First line")
writer.WriteLine("Second line")
writer.Write("No newline")
End Using
' append
File.AppendAllText("log.txt", $"{Date.Now}: message{Environment.NewLine}")
' async I/O (don't block UI)
Async Function ReadAsync(path As String) As Task(Of String)
Using reader As New StreamReader(path)
Return Await reader.ReadToEndAsync()
End Using
End Function
' write async
Using writer As New StreamWriter("out.txt")
Await writer.WriteLineAsync("async line")
End Using二进制与 CSV
BinaryWriter/Reader 处理类型化二进制数据 —— ReadInt32/ReadDouble 必须完全匹配写入顺序。TextFieldParser(在 Microsoft.VisualBasic.FileIO 中)是解析 CSV 的健壮方式 —— 处理带嵌入逗号和换行符的引号字段。不要用 Split(",") 自行编写 CSV 解析器。对于复杂 CSV,考虑 CsvHelper NuGet 包。
Imports System.IO
Imports Microsoft.VisualBasic.FileIO
' BinaryWriter/Reader
Using fs As New FileStream("data.bin", FileMode.Create),
bw As New BinaryWriter(fs)
bw.Write(42I) ' Integer
bw.Write(3.14R) ' Double
bw.Write("hello") ' String (length-prefixed)
End Using
Using fs As New FileStream("data.bin", FileMode.Open),
br As New BinaryReader(fs)
Dim n = br.ReadInt32()
Dim d = br.ReadDouble()
Dim s = br.ReadString()
End Using
' CSV with TextFieldParser (handles quoted fields)
Using parser As New TextFieldParser("data.csv")
parser.TextFieldType = FieldType.Delimited
parser.SetDelimiters(",")
parser.HasFieldsEnclosedInQuotes = True
' skip header
If Not parser.EndOfData Then parser.ReadFields()
While Not parser.EndOfData
Dim fields = parser.ReadFields()
Console.WriteLine($"Name: {fields(0)}, Age: {fields(1)}")
End While
End Using
' write CSV
Dim lines = {"name,age", "Alice,30", "Bob,25"}
File.WriteAllLines("out.csv", lines)路径操作
始终使用 Path.Combine(而非字符串连接)连接路径 —— 它正确处理分隔符。Path.GetInvalidFileNameChars 帮助为文件名清理用户输入。FileInfo/DirectoryInfo 缓存文件元数据 —— 当需要多个属性时使用它们。SearchOption.AllDirectories 递归;对于巨大的树,使用 EnumerateFiles(流式)。
Imports System.IO
' path manipulation (cross-platform)
Dim full = Path.Combine("C:", "Users", "alice", "data.txt")
' C:Usersalicedata.txt
Dim dir = Path.GetDirectoryName(full) ' C:Usersalice
Dim file = Path.GetFileName(full) ' data.txt
Dim name = Path.GetFileNameWithoutExtension(full) ' data
Dim ext = Path.GetExtension(full) ' .txt
' temp files
Dim tempFile = Path.GetTempFileName() ' creates empty file
Dim tempDir = Path.GetTempPath()
' check extension
If Path.GetExtension(path).Equals(".csv", StringComparison.OrdinalIgnoreCase) Then
' process CSV
End If
' directory operations
Directory.CreateDirectory("path/to/dir")
If Directory.Exists("path") Then
Dim files = Directory.GetFiles("path", "*.txt", SearchOption.AllDirectories)
Dim dirs = Directory.GetDirectories("path")
End If
' file info
Dim fi As New FileInfo("data.txt")
Console.WriteLine($"Size: {fi.Length}, Modified: {fi.LastWriteTime}")
' safe file name
Dim invalid = Path.GetInvalidFileNameChars()
Dim safe = New String(name.Select(Function(c) If(invalid.Contains(c), "_"c, c)).ToArray())文件监视
FileSystemWatcher 监视目录的更改。设置 NotifyFilter 限制哪些更改触发事件(性能)。最大的陷阱:编辑器通常每次保存触发多个 Changed 事件(写入、刷新、元数据)—— 通过跟踪最后事件时间来防抖。错误(如来自太多事件的缓冲区溢出)也需要处理。
Imports System.IO
' FileSystemWatcher monitors directory changes
Dim watcher As New FileSystemWatcher() With {
.Path = "C:Watch",
.Filter = "*.txt",
.IncludeSubdirectories = True,
.NotifyFilter = NotifyFilters.FileName Or NotifyFilters.LastWrite Or NotifyFilters.Size
}
' event handlers
AddHandler watcher.Created, Sub(s, e)
Console.WriteLine($"Created: {e.FullPath}")
End Sub
AddHandler watcher.Changed, Sub(s, e)
Console.WriteLine($"Changed: {e.FullPath}")
End Sub
AddHandler watcher.Deleted, Sub(s, e)
Console.WriteLine($"Deleted: {e.FullPath}")
End Sub
AddHandler watcher.Renamed, Sub(s, e)
Console.WriteLine($"Renamed: {e.OldFullPath} -> {e.FullPath}")
End Sub
AddHandler watcher.Error, Sub(s, e)
Console.WriteLine($"Error: {e.GetException().Message}")
End Sub
watcher.EnableRaisingEvents = True
' common gotcha: multiple events fire for one save
' debounce by tracking last event time
Dim lastEvent As DateTime
AddHandler watcher.Changed, Sub(s, e)
If (DateTime.Now - lastEvent).TotalMilliseconds > 500 Then
lastEvent = DateTime.Now
' process
End If
End Sub序列化
System.Text.Json 是现代、高性能的 JSON 序列化器(为新代码替换 Newtonsoft.Json)。XmlSerializer 处理 XML —— 注意它需要无参构造函数和公共属性。对于集合,直接序列化 List(Of T)。带 WriteIndented 的 JsonSerializerOptions 产生人类可读的输出。
Imports System.IO
Imports System.Xml.Serialization
Imports System.Text.Json
' class to serialize
Public Class Person
Public Property Name As String
Public Property Age As Integer
Public Property Email As String
End Class
' JSON (System.Text.Json — modern, fast)
Dim p As New Person With {.Name = "Alice", .Age = 30, .Email = "[email protected]"}
Dim json = JsonSerializer.Serialize(p)
File.WriteAllText("p.json", json)
Dim jsonOpts As New JsonSerializerOptions With {.WriteIndented = True}
File.WriteAllText("p_pretty.json", JsonSerializer.Serialize(p, jsonOpts))
Dim loaded = JsonSerializer.Deserialize(Of Person)(File.ReadAllText("p.json"))
' XML
Dim xs As New XmlSerializer(GetType(Person))
Using fs As New FileStream("p.xml", FileMode.Create)
xs.Serialize(fs, p)
End Using
Using fs As New FileStream("p.xml", FileMode.Open)
Dim loadedXml = DirectCast(xs.Deserialize(fs), Person)
End Using
' collections
Dim people As New List(Of Person) From {p, New Person With {.Name = "Bob"}}
Dim jsonList = JsonSerializer.Serialize(people)
Dim loadedList = JsonSerializer.Deserialize(Of List(Of Person))(jsonList)错误处理深入
Try/Catch 模式
在通用异常之前捕获特定异常(顺序很重要)。使用 Finally 进行清理 —— 即使在 Try 内部返回或抛出时它也会运行。When 过滤器(VB 14+)让你基于异常属性捕获。包装异常时,将原始异常作为 innerException 传递以保留堆栈跟踪。除非重新抛出,否则避免捕获 Exception。
Try
Dim result = 10 / x
File.WriteAllText("out.txt", result.ToString())
Catch ex As DivideByZeroException
Console.WriteLine($"Cannot divide by zero: {ex.Message}")
Catch ex As IOException
Console.WriteLine($"File error: {ex.Message}")
Catch ex As UnauthorizedAccessException
Console.WriteLine($"Permission denied: {ex.Message}")
Catch ex As Exception
' catch-all (use sparingly)
Console.WriteLine($"Unexpected: {ex.Message}")
Throw ' re-throw
Finally
' always runs (even on exception or return)
' cleanup: close files, release resources
End Try
' exception filters (VB 14+)
Try
ProcessData()
Catch ex As InvalidOperationException When ex.Message.Contains("collection")
Console.WriteLine("Collection was modified")
Catch ex As InvalidOperationException
Console.WriteLine("Other invalid op")
End Try
' nested try
Try
Try
RiskyOperation()
Catch ex As Exception
Throw New InvalidOperationException("Inner failed", ex) ' wrap
End Try
Catch ex As Exception
Console.WriteLine(ex.InnerException?.Message)
End Try自定义异常
自定义异常应继承自 Exception(或领域特定的基类)并提供多个构造函数:仅消息、消息 + 内部异常、领域特定。如果可能跨 AppDomain 边界,标记 <Serializable>。为调用者需要的上下文添加属性。重写 ToString 以便清晰记录日志。
' inherit from Exception (or a more specific base)
<Serializable>
Public Class ValidationException
Inherits Exception
Public Property FieldName As String
Public Sub New(message As String)
MyBase.New(message)
End Sub
Public Sub New(message As String, inner As Exception)
MyBase.New(message, inner)
End Sub
Public Sub New(fieldName As String, message As String)
MyBase.New(message)
Me.FieldName = fieldName
End Sub
Public Overrides Function ToString() As String
Return $"Validation error in '{FieldName}': {Message}"
End Function
End Class
' usage
Sub ValidateAge(age As Integer)
If age < 0 Then
Throw New ValidationException("age", "Age cannot be negative")
ElseIf age > 150
Throw New ValidationException("age", "Age seems unrealistic")
End If
End Sub
' catch custom
Try
ValidateAge(-5)
Catch ex As ValidationException
Console.WriteLine($"Field '{ex.FieldName}': {ex.Message}")
End Try异常处理策略
切勿静默吞掉异常 —— 至少记录它们。对 IDisposable 资源使用 Using(比 try/finally 更干净)。为 AppDomain.UnhandledException(最后机会)和 Application.ThreadException(WinForms UI 线程)注册全局处理器。AggregateException 包装来自并行代码的多个异常 —— 展平并处理每个 InnerException。
' 1. Don't catch what you can't handle
' BAD: swallows errors silently
Try
SaveData()
Catch ex As Exception
' nothing
End Try
' GOOD: log and rethrow, or handle meaningfully
Try
SaveData()
Catch ex As IOException
Logger.Warn(ex, "Save failed, will retry")
RetrySave()
Catch ex As Exception
Logger.Error(ex, "Unexpected error saving")
Throw
End Try
' 2. Use Using instead of try/finally for IDisposable
Using fs As New FileStream("f.txt", FileMode.Open)
' use fs
End Using ' automatically disposes
' 3. Global exception handlers
AddHandler AppDomain.CurrentDomain.UnhandledException, Sub(s, e)
Logger.Fatal(DirectCast(e.ExceptionObject, Exception), "App crash")
End Sub
AddHandler Application.ThreadException, Sub(s, e)
Logger.Error(e.Exception, "UI thread error")
End Sub
' 4. AggregateException (parallel)
Try
Parallel.ForEach(items, Sub(item) ProcessItem(item))
Catch ex As AggregateException
For Each inner In ex.InnerExceptions
Logger.Error(inner, "Task failed")
Next
End TryResult 模式(函数式)
Result 模式将成功/失败作为值返回而非抛出 —— 适用于预期失败(解析、验证),其中异常代价高昂且嘈杂。与扩展方法(OnSuccess、OnFailure)组合进行链式调用。对真正异常的情况使用异常;对可预测的失败使用 Result。
' Alternative to exceptions for expected failures
Public Class Result(Of T)
Public ReadOnly Property Value As T
Public ReadOnly Property [Error] As String
Public ReadOnly Property IsSuccess As Boolean
Private Sub New(value As T, [error] As String, isSuccess As Boolean)
Me.Value = value
Me.Error = [error]
Me.IsSuccess = isSuccess
End Sub
Public Shared Function Ok(value As T) As Result(Of T)
Return New Result(Of T)(value, Nothing, True)
End Function
Public Shared Function Fail([error] As String) As Result(Of T)
Return New Result(Of T)(Nothing, [error], False)
End Function
End Class
' usage
Function ParseInt(s As String) As Result(Of Integer)
Dim n As Integer
If Integer.TryParse(s, n) Then
Return Result(Of Integer).Ok(n)
Else
Return Result(Of Integer).Fail($"Cannot parse '{s}'")
End If
End Function
' caller
Dim result = ParseInt("42")
If result.IsSuccess Then
Console.WriteLine($"Got: {result.Value}")
Else
Console.WriteLine($"Error: {result.Error}")
End If
' chain (extension methods)
' result.OnSuccess(Function(x) x * 2).OnFailure(Function(e) 0)日志集成
使用日志框架(NLog、Serilog、log4net)—— 而非 Console.WriteLine。日志级别(Trace/Debug/Info/Warn/Error/Fatal)让你在运行时通过配置过滤。结构化日志(Serilog)保留字段名 —— 更适合在 ELK 或 Seq 等工具中搜索。始终在日志消息中包含上下文(用户 ID、操作名称)。
Imports NLog ' or log4net, Serilog
' configure (in app.config or code)
' NLog.config:
' <targets><target name="file" type="File" fileName="app.log" /></targets>
' <rules><logger name="*" minlevel="Info" writeTo="file" /></rules>
Public Class MyService
Private Shared ReadOnly Logger As Logger = LogManager.GetCurrentClassLogger()
Public Sub ProcessData(data As String)
Logger.Info($"Processing: {data}")
Try
Logger.Debug("Starting parse")
Dim result = Parse(data)
Logger.Info($"Done: {result}")
Catch ex As FormatException
Logger.Warn(ex, "Parse failed")
Throw
Catch ex As Exception
Logger.Error(ex, "Unexpected error")
Throw
End Try
End Sub
End Class
' structured logging (Serilog)
' Log.Information("User {UserId} logged in from {IP}", userId, ip)
' log levels (in order):
' Trace < Debug < Info < Warn < Error < Fatal
' set minimum level in config to filter
' exception with context
Try
SaveUser(user)
Catch ex As Exception
Logger.Error(ex, "Failed to save user {UserId}", user.Id)
Throw
End TryADO.NET 数据库
连接与命令
始终使用参数(切勿字符串连接)防止 SQL 注入。Using 块确保即使在异常时连接也会关闭。ExecuteReader 用于 SELECT;ExecuteNonQuery 用于 INSERT/UPDATE/DELETE;ExecuteScalar 用于单个值(如 COUNT 或 SCOPE_IDENTITY)。连接池是自动的 —— 晚打开,早关闭。
Imports System.Data.SqlClient ' or Microsoft.Data.SqlClient for .NET+
' or MySql.Data.MySqlClient, System.Data.SQLite, etc.
Dim connStr As String = "Server=localhost;Database=mydb;Integrated Security=True;"
' basic query
Using conn As New SqlConnection(connStr)
conn.Open()
Using cmd As New SqlCommand("SELECT Id, Name FROM Users WHERE Age > @age", conn)
cmd.Parameters.AddWithValue("@age", 18)
Using reader = cmd.ExecuteReader()
While reader.Read()
Console.WriteLine($"{reader("Id")}: {reader("Name")}")
End While
End Using
End Using
End Using
' insert with parameters
Using conn As New SqlConnection(connStr), cmd As New SqlCommand(
"INSERT INTO Users (Name, Age) VALUES (@name, @age); SELECT SCOPE_IDENTITY();", conn)
cmd.Parameters.AddWithValue("@name", "Alice")
cmd.Parameters.AddWithValue("@age", 30)
conn.Open()
Dim newId = CInt(cmd.ExecuteScalar())
End Using
' update/delete
Using cmd As New SqlCommand("UPDATE Users SET Age = @age WHERE Id = @id", conn)
cmd.Parameters.AddWithValue("@age", 31)
cmd.Parameters.AddWithValue("@id", newId)
Dim rowsAffected = cmd.ExecuteNonQuery()
End Using事务
事务确保原子性 —— 所有操作成功或全部失败。单个连接上的 BeginTransaction 用于一个数据库。TransactionScope 处理跨多个连接(甚至多个数据库)的分布式事务 —— 调用 Complete 提交。仔细选择隔离级别:Serializable 最安全但最慢;ReadCommitted 是默认值。
Dim connStr = "Server=localhost;Database=mydb;Integrated Security=True;"
' basic transaction
Using conn As New SqlConnection(connStr)
conn.Open()
Using tran = conn.BeginTransaction()
Try
Using cmd As New SqlCommand("", conn, tran)
cmd.CommandText = "UPDATE Accounts SET Balance = Balance - 100 WHERE Id = 1"
cmd.ExecuteNonQuery()
cmd.CommandText = "UPDATE Accounts SET Balance = Balance + 100 WHERE Id = 2"
cmd.ExecuteNonQuery()
tran.Commit()
End Using
Catch ex As Exception
tran.Rollback()
Console.WriteLine($"Transaction rolled back: {ex.Message}")
Throw
End Try
End Using
End Using
' TransactionScope (distributed transactions)
Using scope As New TransactionScope()
Using conn1 As New SqlConnection(connStr1)
' work on db1
End Using
Using conn2 As New SqlConnection(connStr2)
' work on db2
End Using
scope.Complete() ' commit all
End Using ' rollback if Complete not called
' isolation levels
Using tran = conn.BeginTransaction(IsolationLevel.Serializable)
' ...
End UsingDataTable 与 DataAdapter
DataTable 是内存中的表 —— 与数据库断开连接。SqlDataAdapter 填充它并推送更新回去。SqlCommandBuilder 为简单的单表场景自动生成 INSERT/UPDATE/DELETE 命令。DataView 过滤/排序而无需重新查询。对于新代码,优先使用 ORM(EF Core、Dapper)而非原始 DataTable。
Dim connStr = "Server=localhost;Database=mydb;Integrated Security=True;"
' fill DataTable
Dim dt As New DataTable()
Using adapter As New SqlDataAdapter("SELECT * FROM Users", connStr)
adapter.Fill(dt)
End Using
' access rows
For Each row As DataRow In dt.Rows
Console.WriteLine($"{row("Id")}: {row("Name")}")
Next
' filter and sort (in-memory)
Dim view As New DataView(dt) With {
.Sort = "Name ASC",
.RowFilter = "Age > 25"
}
For Each row As DataRowView In view
Console.WriteLine(row("Name"))
Next
' modify and update
Dim newRow = dt.NewRow()
newRow("Name") = "Carol"
newRow("Age") = 28
dt.Rows.Add(newRow)
dt.Rows(0)("Age") = 31 ' modify
dt.Rows(1).Delete() ' mark for deletion
' push changes back to DB
Using adapter As New SqlDataAdapter("SELECT * FROM Users", connStr)
Dim builder As New SqlCommandBuilder(adapter)
adapter.Update(dt) ' generates INSERT/UPDATE/DELETE
End Using
' LINQ on DataTable
Dim young = From row In dt.AsEnumerable()
Where row.Field(Of Integer)("Age") < 30
Select row.Field(Of String)("Name")Dapper 微型 ORM
Dapper 是原始 ADO.NET(快速但冗长)和 EF Core(高效但较慢)之间的最佳点。它自动将列映射到属性。使用匿名对象作为参数。Query<T> 用于列表;QuerySingle<T> 用于单个;Execute 用于非查询。向 Execute 传递集合进行批量操作。多映射处理连接。
Imports Dapper ' NuGet: Install-Package Dapper
' Dapper is a lightweight ORM that extends IDbConnection
Dim connStr = "Server=localhost;Database=mydb;Integrated Security=True;"
Using conn As New SqlConnection(connStr)
' query
Dim users = conn.Query(Of User)("SELECT * FROM Users WHERE Age > @age",
New With {Key .age = 18}).ToList()
' single
Dim user = conn.QuerySingle(Of User)("SELECT * FROM Users WHERE Id = @id",
New With {Key .id = 5})
' execute
Dim rows = conn.Execute("INSERT INTO Users (Name, Age) VALUES (@name, @age)",
New With {Key .name = "Alice", Key .age = 30})
' batch insert
Dim newUsers = {
New With {Key .name = "Bob", Key .age = 25},
New With {Key .name = "Carol", Key .age = 28}
}
conn.Execute("INSERT INTO Users (Name, Age) VALUES (@name, @age)", newUsers)
' multi-result (one-to-many)
Dim sql = "SELECT * FROM Companies; SELECT * FROM Employees;"
Using multi = conn.QueryMultiple(sql)
Dim companies = multi.Read(Of Company)().ToList()
Dim employees = multi.Read(Of Employee)().ToList()
End Using
' stored procedure
Dim result = conn.Query(Of User)("sp_GetUsers", New With {Key .role = "admin"},
commandType:=CommandType.StoredProcedure)
End UsingEF Core 基础
EF Core 是功能齐全的 ORM —— 处理关系、迁移、变更跟踪和 LINQ 查询。DbSet 属性是入口点。SaveChanges 持久化所有跟踪的更改。Include 预加载相关实体(避免 N+1 查询)。对于只读场景,使用 AsNoTracking() 获得更好的性能。迁移管理架构更改。
Imports Microsoft.EntityFrameworkCore
' DbContext
Public Class AppDbContext
Inherits DbContext
Public Property Users As DbSet(Of User)
Public Property Orders As DbSet(Of Order)
Protected Overrides Sub OnConfiguring(options As DbContextOptionsBuilder)
options.UseSqlServer("Server=localhost;Database=mydb;Integrated Security=True;")
End Sub
Protected Overrides Sub OnModelCreating(modelBuilder As ModelBuilder)
modelBuilder.Entity(Of User)().
HasKey(Function(u) u.Id).
HasMany(Function(u) u.Orders).
WithOne(Function(o) o.User).
HasForeignKey(Function(o) o.UserId)
End Sub
End Class
' CRUD
Using db As New AppDbContext()
' create
db.Users.Add(New User With {.Name = "Alice", .Age = 30})
db.SaveChanges()
' read
Dim alice = db.Users.First(Function(u) u.Name = "Alice")
Dim adults = db.Users.Where(Function(u) u.Age >= 18).ToList()
' update
alice.Age = 31
db.SaveChanges()
' delete
db.Users.Remove(alice)
db.SaveChanges()
' eager loading
Dim usersWithOrders = db.Users.Include(Function(u) u.Orders).ToList()
' raw SQL
Dim raw = db.Users.FromSqlRaw("SELECT * FROM Users WHERE Age > {0}", 18).ToList()
' transactions
Using tran = db.Database.BeginTransaction()
Try
' work
db.SaveChanges()
tran.Commit()
Catch
tran.Rollback()
Throw
End Try
End Using
End UsingVB 中的 LINQ
查询语法
VB 的 LINQ 查询语法类似 SQL,对复杂查询通常更可读。From ... Where ... Select 是基本模式。多个 From 子句执行交叉连接(用 Where 过滤)。Group By 创建可通过 Group 关键字访问的组。Join 需要 Equals 关键字(而非 SQL 中的 On ... Equals ...)。
Dim nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
' query syntax (SQL-like)
Dim evens = From n In nums
Where n Mod 2 = 0
Select n
' List: 2, 4, 6, 8, 10
Dim squares = From n In nums
Select n, Square = n * n
Dim pairs = From n In nums
Where n > 3
Order By n Descending
Select n
' with multiple sources
Dim names = {"Alice", "Bob", "Carol"}
Dim ages = {30, 25, 42}
Dim people = From name In names
From age In ages
Where age > 26
Select name, age
' group by
Dim orders = GetOrders()
Dim byCustomer = From o In orders
Group o By o.CustomerId Into Group
Select CustomerId, Total = Group.Sum(Function(o) o.Amount)
' join
Dim result = From u In users
Join o In orders On u.Id Equals o.UserId
Select u.Name, o.Amount方法语法
方法语法使用扩展方法和 lambda —— 比查询语法更紧凑且可组合。Function(x) ... 是 VB 中的 lambda 语法。空时 First 抛出异常;FirstOrDefault 返回默认值(引用类型为 Nothing,Integer 为 0)。Skip/Take 是标准的分页模式。两种语法编译为相同的 IL。
Dim nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
' method syntax (fluent)
Dim evens = nums.Where(Function(n) n Mod 2 = 0).ToList()
Dim squares = nums.Select(Function(n) n * n).ToList()
Dim sorted = nums.OrderBy(Function(n) n).ToList()
Dim desc = nums.OrderByDescending(Function(n) n).ToList()
' aggregation
Dim sum = nums.Sum()
Dim avg = nums.Average()
Dim min = nums.Min()
Dim max = nums.Max()
Dim count = nums.Count()
Dim countEven = nums.Count(Function(n) n Mod 2 = 0)
' element access
Dim first = nums.First()
Dim firstEven = nums.First(Function(n) n Mod 2 = 0)
Dim maybeFirst = nums.FirstOrDefault(Function(n) n > 100) ' 0 if not found
Dim single = nums.Single(Function(n) n = 5)
' take/skip
Dim top3 = nums.Take(3).ToList()
Dim skip3 = nums.Skip(3).ToList()
Dim page = nums.Skip(10).Take(5).ToList() ' pagination
' distinct
Dim unique = nums.Distinct().ToList()
Dim union = nums.Union({11, 12}).ToList()
Dim intersect = nums.Intersect({5, 6, 7}).ToList()
Dim except = nums.Except({5, 6, 7}).ToList()分组与聚合
GroupBy 返回 IGrouping(Of Key, Element) —— 每个组有一个 Key 且本身可枚举。ToLookup 创建查找表(类似多值字典)。匿名类型(New With {Key .X = ...})非常适合投影 —— Key 使属性不可变并成为相等性的一部分。Aggregate 是最灵活(但最不可读)的组合器。
Public Class Order
Public Property Id As Integer
Public Property CustomerId As Integer
Public Property Amount As Decimal
Public Property Date As DateTime
End Class
Dim orders = GetOrders()
' group by
Dim byCustomer = orders.GroupBy(Function(o) o.CustomerId)
For Each grp In byCustomer
Console.WriteLine($"Customer {grp.Key}: {grp.Count()} orders, total {grp.Sum(Function(o) o.Amount)}")
Next
' group with projection
Dim summary = orders.GroupBy(Function(o) o.CustomerId).
Select(Function(g) New With {
Key .CustomerId = g.Key,
Key .OrderCount = g.Count(),
Key .TotalAmount = g.Sum(Function(o) o.Amount),
Key .AvgAmount = g.Average(Function(o) o.Amount),
Key .MaxAmount = g.Max(Function(o) o.Amount)
}).ToList()
' multi-level grouping
Dim byMonth = orders.GroupBy(Function(o) New With {Key o.Date.Year, Key o.Date.Month})
For Each grp In byMonth
Console.WriteLine($"{grp.Key.Year}-{grp.Key.Month}: {grp.Sum(Function(o) o.Amount)}")
Next
' lookup (like a dictionary of lists)
Dim lookup = orders.ToLookup(Function(o) o.CustomerId)
Dim customerOrders = lookup(5).ToList() ' all orders for customer 5
' aggregate with seed
Dim total = orders.Aggregate(0D, Function(acc, o) acc + o.Amount)连接数据
Join 是内连接(仅匹配)。Group Join 配合 DefaultIfEmpty 给出左连接。多个 Join 子句自然链式。方法语法 Join 接受四个 lambda:外键、内键、结果选择器。Zip 按位置配对元素 —— 有用但罕见。对于复杂连接,查询语法通常比方法语法更清晰。
Public Class User
Public Property Id As Integer
Public Property Name As String
Public Property DepartmentId As Integer
End Class
Public Class Department
Public Property Id As Integer
Public Property Name As String
End Class
Dim users = GetUsers()
Dim departments = GetDepartments()
' inner join
Dim result = From u In users
Join d In departments On u.DepartmentId Equals d.Id
Select u.Name, Department = d.Name
' method syntax
Dim result2 = users.Join(departments,
Function(u) u.DepartmentId,
Function(d) d.Id,
Function(u, d) New With {Key .Name = u.Name, Key .Dept = d.Name}).ToList()
' group join (left join)
Dim leftJoin = From d In departments
Group Join u In users On d.Id Equals u.DepartmentId Into Group
From u In Group.DefaultIfEmpty()
Select Department = d.Name, User = If(u Is Nothing, "(none)", u.Name)
' multiple joins
Dim detail = From u In users
Join d In departments On u.DepartmentId Equals d.Id
Join o In orders On u.Id Equals o.UserId
Select u.Name, d.Name, o.Amount
' zip (pair two sequences element-by-element)
Dim nums = {1, 2, 3}
Dim words = {"one", "two", "three"}
Dim pairs = nums.Zip(words, Function(n, w) $"{n}={w}").ToList()
' {"1=one", "2=two", "3=three"}延迟执行
LINQ 使用延迟执行 —— 查询在迭代时运行,而非定义时。这意味着每次 For Each 重新运行查询。要缓存结果,调用 ToList/ToArray。避免在查询 lambda 中有副作用(它们不可预测地运行)。某些运算符(Count、First、Any)强制立即执行。Any/All 短路。
Dim nums = New List(Of Integer) From {1, 2, 3, 4, 5}
' query is NOT executed yet
Dim query = nums.Where(Function(n) n > 2).Select(Function(n) n * 10)
' executes when iterated
For Each n In query
Console.WriteLine(n) ' 30, 40, 50
Next
' executes AGAIN on each iteration
For Each n In query
Console.WriteLine(n) ' 30, 40, 50 again
Next
' force immediate execution
Dim list = query.ToList() ' executes once, caches
Dim arr = query.ToArray()
Dim count = query.Count() ' executes
Dim first = query.First() ' executes
' side effects in query — be careful!
Dim i = 0
Dim bad = nums.Where(Function(n)
i += 1
Return n > 2
End Function)
' i changes every time you iterate bad
' eager vs deferred operators
' Deferred: Where, Select, OrderBy, Skip, Take, Distinct
' Eager: ToList, ToArray, Count, First, Sum, Max, Any, All
' check if any/all match
Dim hasEven = nums.Any(Function(n) n Mod 2 = 0)
Dim allPositive = nums.All(Function(n) n > 0)多线程
Task 与异步
Task.Run 在线程池线程上调度工作。Async/Await 使异步代码看起来同步 —— 方法在 Await 处暂停而不阻塞线程。Task.WhenAll 等待所有;Task.WhenAny 返回第一个完成的。始终向长时间运行的任务传递 CancellationToken 并定期调用 ThrowIfCancellationRequested。
Imports System.Threading.Tasks
' Task creation
Dim t1 = Task.Run(Function() DoWork())
Dim t2 = Task.Run(Function() 42) ' returns Integer
Dim result = Await t2 ' 42
' async function
Async Function FetchDataAsync() As Task(Of String)
Using client As New HttpClient()
Return Await client.GetStringAsync("https://api.example.com")
End Using
End Function
' call it
Dim data = Await FetchDataAsync()
' parallel execution
Dim tasks = {
Task.Run(Function() ComputePart1()),
Task.Run(Function() ComputePart2()),
Task.Run(Function() ComputePart3())
}
Dim results = Await Task.WhenAll(tasks)
' when any (first to finish)
Dim allTasks = urls.Select(Function(u) FetchAsync(u))
Dim firstDone = Await Task.WhenAny(allTasks)
Dim firstResult = Await firstDone
' continuation
Dim t = Task.Run(Function() 42).
ContinueWith(Function(prev) prev.Result * 2)
Console.WriteLine(t.Result) ' 84
' cancellation
Dim cts As New CancellationTokenSource()
Dim token = cts.Token
Dim task = Task.Run(Function()
For i = 1 To 100
token.ThrowIfCancellationRequested()
Thread.Sleep(100)
Next
End Function, token)
cts.CancelAfter(500) ' cancel after 500ms并行循环
Parallel.For/ForEach 跨线程池线程分区工作 —— 非常适合 CPU 密集型循环。线程局部重载用于归约(sum、max)而无需每次迭代锁定。state.Break 停止更高的迭代;state.Stop 立即停止。PLINQ(AsParallel)并行化 LINQ —— 但仅对 CPU 密集型操作有帮助。
Imports System.Threading.Tasks
' Parallel.For
Parallel.For(0, 100, Sub(i)
ProcessItem(i)
End Sub)
' with options
Dim opts As New ParallelOptions With {
.MaxDegreeOfParallelism = Environment.ProcessorCount
}
Parallel.For(0, 100, opts, Sub(i)
ProcessItem(i)
End Sub)
' Parallel.ForEach
Dim items = GetItems()
Parallel.ForEach(items, Sub(item)
ProcessItem(item)
End Sub)
' with state (thread-local)
Dim total As Integer = 0
Dim lockObj As New Object()
Parallel.For(0, 1000,
Function() 0, ' local init
Function(i, state, localTotal)
localTotal += Compute(i)
Return localTotal
End Function, ' body
Sub(localTotal)
SyncLock lockObj
total += localTotal
End SyncLock
End Sub ' local finally
)
' break vs stop
Parallel.For(0, 100, Sub(i, state)
If i = 50 Then state.Break() ' stop iterations > 50
If i = 25 Then state.Stop() ' stop all immediately
End Sub)
' PLINQ
Dim nums = Enumerable.Range(1, 1000)
Dim squares = nums.AsParallel().
Where(Function(n) n Mod 2 = 0).
Select(Function(n) n * n).
ToList()同步
SyncLock 是最简单的同步 —— 互斥。Interlocked 用于原子 int/long 操作(比锁快)。Mutex 可以命名并跨进程共享(单实例应用)。Semaphore 限制并发(例如,最多 3 个连接)。ReaderWriterLockSlim 允许多个读取者或一个写入者 —— 非常适合读密集型缓存。
Imports System.Threading
' SyncLock (Monitor.Enter/Exit)
Private ReadOnly lockObj As New Object()
Private counter As Integer = 0
Sub Increment()
SyncLock lockObj
counter += 1
End SyncLock
End Sub
' Interlocked (atomic operations)
Interlocked.Increment(counter)
Interlocked.Decrement(counter)
Interlocked.Add(counter, 10)
Interlocked.Exchange(counter, 0)
Dim oldVal = Interlocked.CompareExchange(counter, 42, 0) ' if 0, set to 42
' Mutex (cross-process)
Using mtx As New Mutex(False, "Global\MyAppMutex")
If mtx.WaitOne(0) Then
' got the mutex — only one instance runs
Else
' another instance is running
End If
End Using
' Semaphore (limit concurrent access)
Dim sem As New Semaphore(3, 3) ' 3 concurrent
sem.WaitOne()
Try
' work
Finally
sem.Release()
End Try
' ReaderWriterLockSlim
Dim rwLock As New ReaderWriterLockSlim()
rwLock.EnterReadLock()
Try
' multiple readers
Finally
rwLock.ExitReadLock()
End Try
rwLock.EnterWriteLock()
Try
' exclusive write
Finally
rwLock.ExitWriteLock()
End Try通道与生产者-消费者
Channel<T>(现代)是构建生产者-消费者管道的推荐方式 —— 完全异步,通过有界容量支持背压。ForEachAsync(并行消费)允许多个消费者并发读取。BlockingCollection 是较旧的同步等价物。ConcurrentQueue/Stack 是直接使用的无锁集合。
Imports System.Threading.Channels
' create a channel (bounded for backpressure)
Dim channel = Channel.CreateBounded(Of Integer)(100)
' producer
Async Function ProducerAsync() As Task
For i = 1 To 1000
Await channel.Writer.WriteAsync(i)
Next
channel.Writer.Complete()
End Function
' consumer
Async Function ConsumerAsync() As Task
Await ForEachAsync(channel.Reader.ReadAllAsync(),
Environment.ProcessorCount,
Async Function(item)
Await ProcessItemAsync(item)
End Function)
End Function
' multiple producers/consumers
Dim producers = Enumerable.Range(0, 3).Select(Function(i) ProducerAsync())
Dim consumers = Enumerable.Range(0, 3).Select(Function(i) ConsumerAsync())
Await Task.WhenAll(producers.Concat(consumers))
' BlockingCollection (older API, still useful)
Dim bc As New BlockingCollection(Of Integer)(100)
' producer
Task.Run(Sub()
For i = 1 To 100
bc.Add(i)
Next
bc.CompleteAdding()
End Sub)
' consumer
Task.Run(Sub()
For Each item In bc.GetConsumingEnumerable()
ProcessItem(item)
Next
End Sub)
' thread-safe queue
Dim queue As New ConcurrentQueue(Of Integer)()
queue.Enqueue(1)
Dim val As Integer
queue.TryDequeue(val)计时器与调度
三种计时器类型:System.Threading.Timer(线程池,最高效)、System.Timers.Timer(服务器场景,事件)、Windows.Forms.Timer(UI 线程,用于 UI 更新)。对于异步代码,Task.Delay 比计时器更干净。始终处置计时器以防止泄漏。对于周期性异步工作,带 Task.Delay 和 CancellationToken 的 While 循环是最干净的模式。
Imports System.Threading
' System.Threading.Timer (background thread)
Dim timer As New Timer(Sub(state)
Console.WriteLine($"Tick at {DateTime.Now}")
End Sub, Nothing, TimeSpan.Zero, TimeSpan.FromSeconds(5))
' change interval
timer.Change(TimeSpan.Zero, TimeSpan.FromSeconds(10))
' dispose
timer.Dispose()
' System.Timers.Timer (events, can use SynchronizingObject)
Dim eventTimer As New Timers.Timer With {
.Interval = 5000,
.AutoReset = True
}
AddHandler eventTimer.Elapsed, Sub(s, e)
Console.WriteLine("Elapsed")
End Sub
eventTimer.Start()
' UI timer (WinForms — runs on UI thread)
Dim uiTimer As New Windows.Forms.Timer With {.Interval = 1000}
AddHandler uiTimer.Tick, Sub(s, e)
Label1.Text = DateTime.Now.ToString()
End Sub
uiTimer.Start()
' one-shot delay
Async Function DoLaterAsync() As Task
Await Task.Delay(5000) ' 5 seconds
Console.WriteLine("Done waiting")
End Function
' periodic with cancellation
Async Function PeriodicAsync(token As CancellationToken) As Task
While Not token.IsCancellationRequested
DoWork()
Await Task.Delay(1000, token)
End While
End Function序列化
使用 System.Text.Json 的 JSON
System.Text.Json 是 .NET 内置的现代、快速 JSON 序列化器。特性控制序列化:JsonPropertyName 重命名,JsonIgnore 排除。JsonNamingPolicy.CamelCase 匹配 JavaScript 约定。对于巨大的 JSON,序列化到流(而非字符串)以避免将所有内容加载到内存。JsonDocument 无需目标类型即可解析。
Imports System.Text.Json
Imports System.Text.Json.Serialization
Public Class Person
Public Property Id As Integer
Public Property Name As String
<JsonPropertyName("email")>
Public Property Email As String
<JsonIgnore>
Public Property Password As String
Public Property CreatedAt As DateTime
End Class
' serialize
Dim p As New Person With {.Id = 1, .Name = "Alice", .Email = "[email protected]", .CreatedAt = Date.Now}
Dim json As String = JsonSerializer.Serialize(p)
' with options
Dim opts As New JsonSerializerOptions With {
.WriteIndented = True,
.PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
}
json = JsonSerializer.Serialize(p, opts)
' deserialize
Dim loaded = JsonSerializer.Deserialize(Of Person)(json)
' collections
Dim people As New List(Of Person) From {p}
Dim jsonList = JsonSerializer.Serialize(people)
Dim loadedList = JsonSerializer.Deserialize(Of List(Of Person))(jsonList)
' dynamic (JsonDocument)
Using doc As JsonDocument = JsonDocument.Parse(json)
Dim name = doc.RootElement.GetProperty("name").GetString()
Console.WriteLine(name)
End Using
' stream (large files)
Using fs As New FileStream("data.json", FileMode.Create)
JsonSerializer.Serialize(fs, p)
End UsingXML 序列化
XmlSerializer 冗长但灵活。特性控制元素/属性名称和结构。它需要无参构造函数和公共读/写属性。XmlArray/XmlArrayItem 控制嵌套集合。对于读取大型 XML 文件,优先使用 XmlReader(流式)而非 XmlSerializer(将所有内容加载到内存)。
Imports System.Xml.Serialization
Imports System.IO
<XmlRoot("person")>
Public Class Person
<XmlElement("id")>
Public Property Id As Integer
<XmlElement("name")>
Public Property Name As String
<XmlAttribute("role")>
Public Property Role As String
<XmlIgnore>
Public Property Password As String
<XmlArray("orders")>
<XmlArrayItem("order")>
Public Property Orders As List(Of Order)
End Class
' serialize
Dim xs As New XmlSerializer(GetType(Person))
Using fs As New FileStream("p.xml", FileMode.Create)
xs.Serialize(fs, p)
End Using
' with settings
Dim settings As New XmlWriterSettings With {
.Indent = True,
.IndentChars = " ",
.Encoding = Encoding.UTF8
}
Using fs As New FileStream("p.xml", FileMode.Create),
xw As XmlWriter = XmlWriter.Create(fs, settings)
xs.Serialize(xw, p)
End Using
' deserialize
Using fs As New FileStream("p.xml", FileMode.Open)
Dim loaded = DirectCast(xs.Deserialize(fs), Person)
End Using
' collections
Dim xsList As New XmlSerializer(GetType(List(Of Person)))
Using fs As New FileStream("people.xml", FileMode.Create)
xsList.Serialize(fs, people)
End UsingCSV
TextFieldParser 正确处理带引号的 CSV 字段。对于写入,通过包装在引号中并加倍内部引号来转义包含逗号、引号或换行符的字段。对于生产,使用 CsvHelper(NuGet)—— 处理所有边缘情况,支持自定义映射,且快得多。始终为 CSV 使用库;该格式有令人惊讶的边缘情况。
Imports Microsoft.VisualBasic.FileIO
' read CSV (handles quoted fields)
Using parser As New TextFieldParser("data.csv")
parser.TextFieldType = FieldType.Delimited
parser.SetDelimiters(",")
parser.HasFieldsEnclosedInQuotes = True
While Not parser.EndOfData
Dim fields = parser.ReadFields()
Console.WriteLine($"{fields(0)}, {fields(1)}")
End While
End Using
' write CSV (manual, with proper escaping)
Function CsvEscape(s As String) As String
If s.Contains(",") OrElse s.Contains("""") OrElse s.Contains(vbCrLf) Then
Return $"""{s.Replace("""", """""")}"""
End If
Return s
End Function
Dim lines As New List(Of String)
lines.Add("name,age,email")
For Each p In people
lines.Add($"{CsvEscape(p.Name)},{p.Age},{CsvEscape(p.Email)}")
Next
File.WriteAllLines("out.csv", lines)
' CsvHelper (NuGet) — production-grade
' Install-Package CsvHelper
Imports CsvHelper
Using reader As New StreamReader("data.csv"),
csv As New CsvReader(reader, CultureInfo.InvariantCulture)
Dim records = csv.GetRecords(Of Person)().ToList()
End Using
Using writer As New StreamWriter("out.csv"),
csv As New CsvWriter(writer, CultureInfo.InvariantCulture)
csv.WriteRecords(people)
End Using二进制与 DataContract
DataContractSerializer 比 XmlSerializer 更容忍版本(通过 Order 处理添加/删除的字段)。对于高性能二进制序列化,使用 Protocol Buffers(protobuf-net)或 MessagePack —— 两者都比 XML/JSON 小 10-100 倍且更快。根据需要选择:人类可读(JSON/XML)vs 紧凑/快速(protobuf/messagepack)。
Imports System.Runtime.Serialization
Imports System.IO
' DataContract (version-tolerant)
<DataContract>
Public Class Person
<DataMember(Order:=1)>
Public Property Id As Integer
<DataMember(Order:=2)>
Public Property Name As String
<DataMember(Order:=3, IsRequired:=False)>
Public Property Email As String
End Class
' serialize to XML
Dim ser As New DataContractSerializer(GetType(Person))
Using fs As New FileStream("p.xml", FileMode.Create)
ser.WriteObject(fs, p)
End Using
' deserialize
Using fs As New FileStream("p.xml", FileMode.Open)
Dim loaded = DirectCast(ser.ReadObject(fs), Person)
End Using
' Protocol Buffers (protobuf-net, NuGet)
' Install-Package protobuf-net
<ProtoContract>
Public Class Person
<ProtoMember(1)>
Public Property Id As Integer
<ProtoMember(2)>
Public Property Name As String
End Class
' serialize
Using fs As New FileStream("p.bin", FileMode.Create)
ProtoBuf.Serializer.Serialize(fs, p)
End Using
' deserialize
Using fs As New FileStream("p.bin", FileMode.Open)
Dim loaded = ProtoBuf.Serializer.Deserialize(Of Person)(fs)
End Using
' MessagePack (MessagePack-CSharp, NuGet)
' compact, fast, schema-less
Dim bytes = MessagePack.MessagePackSerializer.Serialize(p)
Dim loaded = MessagePack.MessagePackSerializer.Deserialize(Of Person)(bytes)自定义转换器
自定义转换器处理 System.Text.Json 原生不支持的类型(DateOnly、TimeSpan、自定义类型)。JsonDerivedType(NET 7+)处理多态 —— 用判别器序列化运行时类型。ShouldSerialize* 方法有条件地包含属性(类似 XmlSerializer 的模式)。始终在 JsonSerializerOptions 中注册转换器。
Imports System.Text.Json
Imports System.Text.Json.Serialization
' custom converter for a type
Public Class DateOnlyConverter
Inherits JsonConverter(Of DateOnly)
Public Overrides Function Read(reader As ByRef Utf8JsonReader,
type As Type, options As JsonSerializerOptions) As DateOnly
Return DateOnly.Parse(reader.GetString())
End Function
Public Overrides Sub Write(writer As Utf8JsonWriter, value As DateOnly, options As JsonSerializerOptions)
writer.WriteStringValue(value.ToString("yyyy-MM-dd"))
End Sub
End Class
' register globally
Dim opts As New JsonSerializerOptions()
opts.Converters.Add(New DateOnlyConverter())
' or per-property
Public Class Event
<JsonConverter(GetType(DateOnlyConverter))>
Public Property Date As DateOnly
End Class
' polymorphic serialization
<JsonDerivedType(GetType(Dog), "dog")>
<JsonDerivedType(GetType(Cat), "cat")>
Public MustInherit Class Animal
Public Property Name As String
End Class
Public Class Dog
Inherits Animal
Public Property Breed As String
End Class
Dim a As Animal = New Dog With {.Name = "Rex", .Breed = "Lab"}
Dim json = JsonSerializer.Serialize(a, GetType(Animal))
' {"$type":"dog","name":"Rex","breed":"Lab"}
' conditional serialization
Public Class User
Public Property Name As String
Public Property Password As String
Public Function ShouldSerializePassword() As Boolean
Return False ' never serialize password
End Function
End Class反射
类型检查
反射让你在运行时检查类型。GetType(Type) 用于编译时类型;obj.GetType() 用于运行时类型。BindingFlags 过滤成员(Public/NonPublic、Instance/Static)。GetCustomAttributes 返回应用于类型的特性。反射很慢 —— 缓存 Type 对象和 MemberInfo 以供重复使用。
Imports System.Reflection
Dim t As Type = GetType(String)
' or: Dim t = obj.GetType()
' basic info
Console.WriteLine($"Name: {t.Name}")
Console.WriteLine($"Full: {t.FullName}")
Console.WriteLine($"Base: {t.BaseType}")
Console.WriteLine($"IsClass: {t.IsClass}")
Console.WriteLine($"IsEnum: {t.IsEnum}")
Console.WriteLine($"IsGenericType: {t.IsGenericType}")
' interfaces
For Each i In t.GetInterfaces()
Console.WriteLine($"Implements: {i.Name}")
Next
' properties
For Each prop In t.GetProperties()
Console.WriteLine($"Property: {prop.Name} ({prop.PropertyType.Name})")
Next
' methods
For Each m In t.GetMethods(BindingFlags.Public Or BindingFlags.Instance)
Console.WriteLine($"Method: {m.Name}({String.Join(", ", m.GetParameters().Select(Function(p) p.ParameterType.Name))})")
Next
' fields, constructors, events
t.GetFields()
t.GetConstructors()
t.GetEvents()
' attributes
For Each attr In t.GetCustomAttributes(False)
Console.WriteLine($"Attribute: {attr.GetType().Name}")
Next
' generic type args
If t.IsGenericType Then
For Each arg In t.GetGenericArguments()
Console.WriteLine($"Type arg: {arg.Name}")
Next
End If创建实例与调用
Activator.CreateInstance 按类型创建对象。MethodInfo.Invoke 调用方法 —— 将参数作为 Object() 传递。PropertyInfo.GetValue/SetValue 访问属性。BindingFlags.NonPublic 让你访问私有成员(谨慎使用 —— 破坏封装)。MakeGenericMethod 在运行时构造泛型方法。这是插件系统的基础。
Imports System.Reflection
' create instance
Dim t As Type = GetType(List(Of String))
Dim obj As Object = Activator.CreateInstance(t)
' with constructor args
Dim dt As DateTime = Activator.CreateInstance(GetType(DateTime), {2024, 12, 31})
' invoke method
Dim mi As MethodInfo = t.GetMethod("Add")
mi.Invoke(obj, New Object() {"hello"})
' property get/set
Dim pi As PropertyInfo = t.GetProperty("Count")
Dim count As Integer = CInt(pi.GetValue(obj))
pi.SetValue(obj, 10) ' may throw if read-only
' field get/set
Dim fi As FieldInfo = GetType(MyClass).GetField("_private", BindingFlags.NonPublic Or BindingFlags.Instance)
Dim val = fi.GetValue(instance)
fi.SetValue(instance, newValue)
' generic method
Dim method As MethodInfo = GetType(Enumerable).GetMethod("Where")
Dim generic = method.MakeGenericMethod(GetType(Integer))
Dim filtered = generic.Invoke(Nothing, New Object() {nums, predicate})
' create delegate from method
Dim del As [Delegate] = [Delegate].CreateDelegate(GetType(Func(Of String, Boolean)), mi)
' load assembly
Dim asm As Assembly = Assembly.LoadFrom("plugin.dll")
Dim pluginType As Type = asm.GetType("MyPlugin")
Dim plugin As Object = Activator.CreateInstance(pluginType)特性
自定义特性向类型和成员添加元数据。AttributeUsage 控制它们可以应用的位置以及是否允许多个。GetCustomAttribute/GetCustomAttributes 在运行时通过反射读取它们。内置特性:Obsolete(编译器警告/错误)、Conditional(编译时包含)、Serializable。特性驱动验证、ORM 映射、序列化等。
Imports System
' define custom attribute
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method, AllowMultiple:=True)>
Public Class DescriptionAttribute
Inherits Attribute
Public ReadOnly Property Text As String
Public Sub New(text As String)
Me.Text = text
End Sub
End Class
' apply
<Description("User service")>
Public Class UserService
<Description("Get user by ID")>
Public Function GetUser(id As Integer) As User
' ...
End Function
End Class
' read attributes
Dim t As Type = GetType(UserService)
Dim classAttr = CType(Attribute.GetCustomAttribute(t, GetType(DescriptionAttribute)), DescriptionAttribute)
Console.WriteLine(classAttr?.Text)
For Each mi In t.GetMethods()
Dim methodAttr = CType(Attribute.GetCustomAttribute(mi, GetType(DescriptionAttribute)), DescriptionAttribute)
If methodAttr IsNot Nothing Then
Console.WriteLine($"{mi.Name}: {methodAttr.Text}")
End If
Next
' built-in attributes
<Obsolete("Use NewMethod instead", True)> ' True = error, False = warning
Sub OldMethod()
End Sub
<Conditional("DEBUG")>
Sub LogDebug(msg As String)
Console.WriteLine(msg)
End Sub
<Serializable>
Public Class MyData
End ClassEmit 与动态代码
Reflection.Emit 在运行时生成 IL —— 强大但低级。DefineDynamicAssembly -> Module -> Type -> Method -> ILGenerator。Emit 写入 IL 操作码。表达式树是更高级的替代方案 —— 将表达式构建为数据,然后编译为委托。使用 Emit 获得极致性能(自定义序列化器);表达式树用于动态查询(EF Core)。
Imports System.Reflection
Imports System.Reflection.Emit
' build a dynamic assembly/module/type/method
Dim asmName As New AssemblyName("DynamicAsm")
Dim asm As AssemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.Run)
Dim mod As ModuleBuilder = asm.DefineDynamicModule("MainModule")
Dim type As TypeBuilder = mod.DefineType("Calculator", TypeAttributes.Public)
' add a method
Dim mb As MethodBuilder = type.DefineMethod("Add",
MethodAttributes.Public Or MethodAttributes.Static,
GetType(Integer), {GetType(Integer), GetType(Integer)})
Dim il As ILGenerator = mb.GetILGenerator()
il.Emit(OpCodes.Ldarg_0) ' load first arg
il.Emit(OpCodes.Ldarg_1) ' load second arg
il.Emit(OpCodes.Add) ' add
il.Emit(OpCodes.Ret) ' return
Dim createdType As Type = type.CreateType()
Dim result As Integer = CInt(createdType.GetMethod("Add").Invoke(Nothing, {5, 3}))
Console.WriteLine(result) ' 8
' expression trees (higher-level)
Imports System.Linq.Expressions
Dim xParam As ParameterExpression = Expression.Parameter(GetType(Integer), "x")
Dim yParam As ParameterExpression = Expression.Parameter(GetType(Integer), "y")
Dim body As BinaryExpression = Expression.Add(xParam, yParam)
Dim addFunc As Func(Of Integer, Integer, Integer) =
Expression.Lambda(Of Func(Of Integer, Integer, Integer))(body, {xParam, yParam}).Compile()
Console.WriteLine(addFunc(5, 3)) ' 8性能考虑
反射比直接调用慢 100-1000 倍。始终缓存 MemberInfo 对象。对于热路径,通过表达式树编译为委托 —— 几乎与直接调用一样快。源生成器(现代 .NET)可以在编译时进行反射,完全消除运行时成本。对框架(序列化器、ORM、DI)使用反射;避免在业务逻辑中使用。
Imports System.Reflection
' reflection is slow — cache and optimize
' BAD: re-fetch on every call
For Each item In items
Dim prop = item.GetType().GetProperty("Name")
Dim name = CStr(prop.GetValue(item))
Next
' GOOD: cache PropertyInfo
Dim propInfo As PropertyInfo = GetType(Item).GetProperty("Name")
For Each item In items
Dim name = CStr(propInfo.GetValue(item))
Next
' BEST: compile to delegate (10-100x faster)
Dim getter = CreateGetter(Of Item, String)("Name")
For Each item In items
Dim name = getter(item)
Next
Function CreateGetter(Of T, TResult)(propName As String) As Func(Of T, TResult)
Dim param = Expression.Parameter(GetType(T), "obj")
Dim body = Expression.Property(param, propName)
Return Expression.Lambda(Of Func(Of T, TResult))(body, param).Compile()
End Function
' source generators (modern alternative)
' compile-time reflection — no runtime cost
' Partial Class MyService
' <GenerateReflection>
' Public Shared Sub PrintMethods()
' ' generated code knows the types at compile time
' End Sub
' End Class
' when to use reflection:
' - serializers (JSON, XML)
' - ORMs (mapping columns to properties)
' - DI containers (constructor injection)
' - plugin systems
' - testing frameworks
' avoid for hot paths — cache or compile互操作
P/Invoke(调用 Win32 DLL)
P/Invoke 让你调用原生 DLL 函数。DllImport 指定库和选项。CharSet.Unicode 处理字符串封送。ByRef 用于输出参数。StructLayout(Sequential) 确保结构体匹配 C 布局。SetLastError=True 让你通过 Marshal.GetLastWin32Error() 查询最后的 Win32 错误。始终将原生调用包装在 NativeMethods 类中。
Imports System.Runtime.InteropServices
Public Class NativeMethods
' MessageBox from user32.dll
<DllImport("user32.dll", CharSet:=CharSet.Unicode, SetLastError:=True)>
Public Shared Function MessageBox(hWnd As IntPtr, text As String, caption As String, type As UInteger) As Integer
End Function
' Beep from kernel32
<DllImport("kernel32.dll")>
Public Shared Function Beep(freq As UInteger, duration As UInteger) As Boolean
End Function
' GetSystemMetrics
<DllImport("user32.dll")>
Public Shared Function GetSystemMetrics(nIndex As Integer) As Integer
End Function
End Class
' usage
NativeMethods.MessageBox(IntPtr.Zero, "Hello from native!", "Test", 0)
NativeMethods.Beep(440, 500) ' 440 Hz for 500ms
' constants
Public Const SM_CXSCREEN As Integer = 0
Public Const SM_CYSCREEN As Integer = 1
Dim screenWidth = NativeMethods.GetSystemMetrics(SM_CXSCREEN)
' structures
<StructLayout(LayoutKind.Sequential)>
Public Structure POINT
Public X As Integer
Public Y As Integer
End Structure
<DllImport("user32.dll")>
Public Shared Function GetCursorPos(ByRef lpPoint As POINT) As Boolean
End Function
Dim p As POINT
GetCursorPos(p)
Console.WriteLine($"Cursor at ({p.X}, {p.Y})")COM 互操作
COM 互操作让你调用 Office 和其他 COM 库。添加对互操作程序集的引用(或使用 NuGet)。始终使用 Marshal.ReleaseComObject 释放 COM 对象 —— 它们不会及时被垃圾回收,导致"幽灵"Excel 进程。后期绑定(CreateObject)避免编译时引用但失去 IntelliSense 和类型安全。
Imports System.Runtime.InteropServices
Imports Excel = Microsoft.Office.Interop.Excel
' add reference: Microsoft.Office.Interop.Excel (or via NuGet)
Sub ExportToExcel(data As DataTable)
Dim app As Excel.Application = Nothing
Dim wb As Excel.Workbook = Nothing
Dim ws As Excel.Worksheet = Nothing
Try
app = New Excel.Application()
app.Visible = False
wb = app.Workbooks.Add()
ws = CType(wb.Worksheets(1), Excel.Worksheet)
' write headers
For c = 0 To data.Columns.Count - 1
ws.Cells(1, c + 1) = data.Columns(c).ColumnName
Next
' write data
For r = 0 To data.Rows.Count - 1
For c = 0 To data.Columns.Count - 1
ws.Cells(r + 2, c + 1) = data.Rows(r)(c).ToString()
Next
Next
ws.SaveAs("C: empexport.xlsx")
Finally
' ALWAYS release COM objects
If ws IsNot Nothing Then Marshal.ReleaseComObject(ws)
If wb IsNot Nothing Then
wb.Close(False)
Marshal.ReleaseComObject(wb)
End If
If app IsNot Nothing Then
app.Quit()
Marshal.ReleaseComObject(app)
End If
End Try
End Sub
' late binding (no reference needed)
Dim lateApp As Object = CreateObject("Excel.Application")
lateApp.Visible = True
lateApp.Quit()C# 互操作
VB 和 C# 可以自由互相调用 —— 它们编译为相同的 IL。添加项目引用或共享库。注意大小写敏感性(VB 不区分大小写)、索引语法和 ref/out(VB 使用 ByRef)。C# 的 unsafe 代码、某些泛型和模式匹配等特性在 VB 中没有等价物 —— 对这些部分使用 C#。
' VB and C# interoperate seamlessly in the same project (multi-targeting)
' Add a C# project reference, or use a shared library
' C# code (in CSharpLib.dll):
' namespace Utils
' {
' public class Calculator
' {
' public int Add(int a, int b) => a + b;
' public static int Multiply(int a, int b) => a * b;
' }
' }
' VB usage:
Imports Utils
Dim calc As New Calculator()
Dim sum = calc.Add(2, 3) ' 5
Dim product = Calculator.Multiply(4, 5) ' 20
' differences to watch:
' - VB is case-insensitive; C# is case-sensitive
' - VB uses () for indexing; C# uses []
' - VB And/Or are bitwise (AndAlso/OrElse for short-circuit)
' - C# uses camelCase often; VB PascalCase by convention
' - C# has unsafe code and pointers; VB doesn't
' - C# has yield return; VB has Yield (similar)
' consuming async C# methods
Dim result = Await csharpObj.GetDataAsync()
' ref/out parameters
' C#: void TryParse(string s, out int result)
' VB: Integer.TryParse(s, result) ' result is ByRef
' using C# extension methods
Imports CSharpExtensions
Dim s = "hello".Capitalize() ' extension method from C# lib调用 Python 和其他语言
对于 Python 互操作,Python.NET 在 .NET 中嵌入 CPython —— 直接对象访问。更简单:用 Process.Start 调用 python.exe。R.NET 对 R 做同样的事。ClearScript 为 JavaScript 嵌入 V8。根据集成深度选择:一次性脚本使用 shell 调用;紧密集成使用嵌入。注意性能(跨运行时调用很慢)。