Лямбды, LINQ, операторы и свои коллекции
VB.NET, после процедур и событий и коллекций. Делегаты и
AddressOfкратко — в главе 6.
Лямбды в VB.NET - анонимные функции
Иногда метод нужен один раз — как фильтр списка или обработчик без отдельной Function в модуле. Лямбда — анонимная функция или подпрограмма, которую передают в Where, Sort, AddHandler или в свой делегат.
Синтаксис лямбд
| Форма | Назначение |
|---|---|
Function(x) x * 2 | Выражение, возвращает значение |
Function(x, y) x + y | Несколько параметров |
Sub(x) Console.WriteLine(x) | Без возврата (действие) |
Function(x) … End Function | Многострочное тело |
Imports System
Dim numbers = {3, 1, 4, 1, 5}
Dim doubled = numbers.Select(Function(n) n * 2)
Dim printer As Action(Of String) = Sub(text) Console.WriteLine(text)
printer("готово")
При Option Infer On тип лямбды выводится из контекста (например, из сигнатуры Select).
AddressOf и лямбда
AddressOf ИмяМетода | Лямбда | |
|---|---|---|
| Когда | Уже есть именованный метод | Логика короткая и локальная |
| Читаемость | Явная ссылка на метод формы/класса | Компактно в LINQ |
| События WinForms | Handles или AddHandler …, AddressOf | Редко для UI |
' Именованный обработчик — привычно для кнопок
Private Sub OnSave(sender As Object, e As EventArgs)
SaveData()
End Sub
' Лямбда — удобно для LINQ
Dim adults = people.Where(Function(p) p.Age >= 18)
Func и Action
Встроенные обобщённые делегаты из System:
Dim isEven As Func(Of Integer, Boolean) = Function(n) n Mod 2 = 0
Console.WriteLine(isEven(4)) ' True
Dim log As Action(Of String, Integer) = Sub(msg, code)
Console.WriteLine($"{code}: {msg}")
End Sub
log("старт", 0)
Func(Of T1, …, TResult) — последний тип параметра это результат. Action — только входы, без возврата.
LINQ — запросы к коллекциям
Подключите Imports System.Linq. LINQ работает с IEnumerable(Of T) — массивами, List, результатами чтения файлов.
Синтаксис запросов
Dim query = From p In products
Where p.Price > 1000
Order By p.Name
Select p.Name, p.Price
For Each item In query
Console.WriteLine($"{item.Name} — {item.Price}")
Next
Методы-расширения (чаще в промышленном коде)
Dim names = products.Where(Function(p) p.InStock).
OrderBy(Function(p) p.Name).
Select(Function(p) p.Name).
ToList()
| Метод | Роль |
|---|---|
Where | Фильтр |
Select | Проекция (новая форма элемента) |
OrderBy / OrderByDescending | Сортировка |
First, Single, Any, Count | Поиск и агрегаты |
ToList, ToArray | Материализация результата |
Пустая коллекция: First бросает исключение, FirstOrDefault вернёт Nothing для ссылочных типов.
Dim first = items.FirstOrDefault(Function(x) x.Id = targetId)
If first IsNot Nothing Then
' найдено
End If
LINQ к XML и Entity Framework используют тот же синтаксис From … Where … Select — см. справочник §14 и §18.
Перегрузка операторов
Класс или структура могут задать поведение +, -, = для своих типов. Оператор объявляют Shared, параметры — ByVal.
Public Structure Vector2D
Public ReadOnly X As Double
Public ReadOnly Y As Double
Public Sub New(x As Double, y As Double)
Me.X = x
Me.Y = y
End Sub
Public Shared Operator +(a As Vector2D, b As Vector2D) As Vector2D
Return New Vector2D(a.X + b.X, a.Y + b.Y)
End Operator
Public Shared Operator -(a As Vector2D, b As Vector2D) As Vector2D
Return New Vector2D(a.X - b.X, a.Y - b.Y)
End Operator
End Structure
Dim v = New Vector2D(1, 0) + New Vector2D(0, 1)
Часто перегружают: +, -, =, <>, неявное CType для преобразований. Не стоит перегружать всё подряд — только там, где тип ведёт себя как «значение» в предметной области (деньги, вектор, дата-диапазон).
Индексатор (свойство по умолчанию)
Индексатор даёт доступ объект(ключ) как у массива или словаря.
Public Class Cache
Private ReadOnly _data As New Dictionary(Of String, String)
Default Public Property Item(key As String) As String
Get
Return _data(key)
End Get
Set(value As String)
_data(key) = value
End Set
End Property
End Class
Dim c As New Cache()
c("user:1") = "Anna"
Console.WriteLine(c("user:1"))
Для чтения без исключения при отсутствии ключа внутри Get используйте TryGetValue.
Своя коллекция и For Each
For Each требует, чтобы тип реализовал IEnumerable(Of T) (или необобщённый IEnumerable). Проще всего обернуть внутренний List(Of T) и отдать перечисление через Iterator и Yield.
Imports System.Collections.Generic
Public Class TagList
Implements IEnumerable(Of String)
Private ReadOnly _items As New List(Of String)
Public Sub Add(tag As String)
If Not String.IsNullOrWhiteSpace(tag) Then
_items.Add(tag.Trim())
End If
End Sub
Public ReadOnly Property Count As Integer
Get
Return _items.Count
End Get
End Property
Public Iterator Function GetEnumerator() As IEnumerator(Of String) _
Implements IEnumerable(Of String).GetEnumerator
For Each tag In _items
Yield tag
Next
End Function
End Class
Использование:
Dim tags As New TagList()
tags.Add("vb")
tags.Add("dotnet")
For Each tag In tags
Console.WriteLine(tag)
Next
Dim upper = tags.Select(Function(t) t.ToUpper()).ToList()
Что происходит под капотом
- Компилятор для
For EachвызываетGetEnumerator(). - Перечислитель возвращает элементы по одному (
MoveNext/Currentв классической модели;Iteratorгенерирует это автоматически). - После цикла перечислитель освобождается (
Dispose).
Если коллекция обёртка над List, можно не писать Yield, а вернуть _items.GetEnumerator() — меньше кода, та же семантика для For Each.
Перегрузка методов (напоминание)
Отдельно от операторов — несколько методов с одним именем и разными параметрами:
Public Sub Save(path As String)
Save(path, overwrite:=True)
End Sub
Public Sub Save(path As String, overwrite As Boolean)
' запись на диск
End Sub
Компилятор выбирает версию по числу и типам аргументов. Именованные аргументы (overwrite:=False) улучшают читаемость при многих необязательных параметрах.
Типичные ошибки
| Ошибка | Решение |
|---|---|
Лямбда не компилируется с Option Strict | Явно указать типы параметров: Function(p As Person) |
| Захват переменной цикла в лямбде | В For Each скопировать в локальную: Dim x = item перед лямбдой, если откладываете вызов |
| LINQ «пустой» результат | Проверять Any() или FirstOrDefault |
Бесконечный IEnumerable | ToList() материализует; без этого запрос выполняется при каждом обходе |
Что дальше
- WinForms и события — архитектура, процедуры и события.
- Полные таблицы LINQ и атрибутов — справочник 711.
См. также
Другие статьи этого же раздела в боковом меню (как на странице «О разделе»). История Visual Basic - эволюция от BASIC до современных реализаций в экосистеме Microsoft. Практика VB.NET — консольные программы, dotnet CLI, выбор коллекций и чтение/запись файлов через BCL. Основы языка Visual Basic - синтаксис, базовые конструкции и роль VB в обучении программированию. Архитектура приложений на Visual Basic - структура проекта, компоненты и подходы к построению Windows-приложений. Типизация, набор правил определения типа данных значений языка. Управляющие конструкции и операторы Visual Basic - условные блоки, циклы и выражения в прикладном коде. Процедуры и события в Visual Basic - обработчики, жизненный цикл событий и организация прикладной логики. Гайд по установке и настройке с написанием первой программы и её запуском. Справочник-шпаргалка по visual-basic — типы, синтаксис, стандартная библиотека, типовые паттерны. Не заменяет пошаговое обучение. Учебный курс — раздел. Краткий ввод в Visual Basic for Applications — макросы, объектная модель Excel и отличия от VB.NET. Как в VB.NET устроены value- и reference-типы, приведение CType/TryCast и работа с Nothing. Краткие итоги раздела «Visual Basic».История языка visual-basic
Консоль и файлы
Основы языка visual-basic
Архитектура приложений на visual-basic
Типы данных в visual-basic
Управляющие конструкции и операторы visual-basic
Процедуры и события в visual-basic
Первая программа на visual-basic
Справочник по visual-basic
VBA в Excel
Типы и преобразования
Итоги