Лямбды, 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.
Код ITЗагрузка примера кода…
Часто перегружают — +, -, =, <>, неявное CType для преобразований. Не стоит перегружать всё подряд — только там, где тип ведёт себя как "значение" в предметной области (деньги, вектор, дата-диапазон).
Индексатор (свойство по умолчанию)
Индексатор даёт доступ объект(ключ) как у массива или словаря.
Код ITЗагрузка примера кода…
Для чтения без исключения при отсутствии ключа внутри Get используйте TryGetValue.
Своя коллекция и For Each
For Each требует, чтобы тип реализовал IEnumerable(Of T) (или необобщённый IEnumerable). Проще всего обернуть внутренний List(Of T) и отдать перечисление через Iterator и Yield.
Код ITЗагрузка примера кода…
Использование:
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.