Перейти к основному содержимому

Значимые и ссылочные типы, преобразования

Разработчику

Глава про VB.NET. Базовые типы и Option Strict — в типах данных.

Как читать эту статью

Короткая глава: таблица value и reference → CType / TryCastNothing и nullable. Читайте после типов и перед управлением.

Value и reference types в VB.NET

Путаница между копированием структуры и ссылкой на объект — частая причина багов в VB.NET и при interop с COM.

Value и reference types - зачем разделять

В управляемой среде .NET переменная хранит либо само значение (число, символ, структура), либо ссылку на объект в куче. От этого зависят копирование, сравнение, Nothing и ошибки «приведение невозможно».

КатегорияПримерыГде живёт экземплярПустое состояние
Значимый (Structure, примитив)Integer, Date, Decimal, своя Structure PointОбычно в стеке или внутри объектаНет «пустого» Integer; есть Integer?
Ссылочный (Class, String, массив)String, List(Of T), FormУправляемая кучаNothing

String — ссылочный тип, но ведёт себя как неизменяемое значение: операции создают новую строку.


Копирование и сравнение

' Значимые типы копируются по значению
Dim a As Integer = 10
Dim b As Integer = a
b = 20
' a остаётся 10

' Ссылочные — копируется адрес
Dim list1 As New List(Of String)
list1.Add("A")
Dim list2 As List(Of String) = list1
list2.Add("B")
' list1 тоже содержит "A" и "B"

Для ссылочных типов оператор Is проверяет один и тот же объект в памяти, а = для классов по умолчанию сравнивает ссылки (если не переопределён Equals).

Dim x As New List(Of Integer)
Dim y As List(Of Integer) = x
Console.WriteLine(x Is y) ' True
Console.WriteLine(x Is Nothing) ' False

Structure и Class

StructureClass
НазначениеНебольшие наборы полей (точка, RGB, интервал)Сущности с поведением и жизненным циклом
НаследованиеТолько интерфейсыКлассы и интерфейсы
Передача в процедуруПо значению (копия)По ссылке
NothingНе применяется к самой структуре в переменнойОбнуляет ссылку
Structure Point2D
Public X As Double
Public Y As Double
End Structure

Dim p As Point2D
p.X = 1
p.Y = 2

Для моделей предметной области (Order, Customer) почти всегда выбирают Class.


Упаковка и распаковка

Если значимый тип положить в Object, CLR выполняет boxing — создаёт объект-обёртку в куче. Обратное извлечение — unboxing с явным типом.

Dim boxed As Object = 42
Dim value As Integer = CType(boxed, Integer)

Частые boxing-источники: старые коллекции ArrayList, параметры Object, некоторые перегрузки API. В новом коде предпочтительны List(Of T) и обобщённые методы — меньше лишних аллокаций.


Неявные и явные преобразования

Расширяющие (без потери данных) допускаются неявно, если не мешает Option Strict On:

Dim count As Integer = 10
Dim rate As Double = count ' Integer → Double

Сужающие требуют явного приведения:

Dim price As Double = 19.99
Dim units As Integer = CInt(Math.Floor(price))

При Option Strict On такие присваивания без CInt / CType не компилируются — это защита от тихих округлений.

CType, DirectCast, TryCast

ОператорКогда использовать
CType(expr, T)Известные совместимые типы, числовые преобразования, вызов пользовательских операторов Widening/Narrowing
DirectCast(expr, T)Ссылочные типы в иерархии; если объект не того типа — исключение
TryCast(expr, T)Безопасно: при неудаче вернёт Nothing вместо исключения
Dim obj As Object = "hello"
Dim text As String = TryCast(obj, String) ' "hello"
Dim num As Integer? = TryCast(obj, Integer) ' Nothing

Dim control As Control = DirectCast(obj, Control) ' InvalidCastException для строки

TypeOf и Is

If TypeOf sender Is Button Then
Dim btn = DirectCast(sender, Button)
btn.Enabled = False
End If

TypeOf … Is не бросает исключение при Nothing.


Nothing, nullable и DBNull

  • Nothing для ссылочного типа — «ссылки нет». Для String это не то же самое, что "".
  • Integer? и другие T? — «значение отсутствует» через HasValue / Nothing.
  • DBNull.Value — отдельный маркер из ADO.NET при чтении NULL из БД; сравнивайте через IsDBNull, не смешивайте с Nothing без проверки.
Dim age As Integer? = Nothing
If age.HasValue Then
Console.WriteLine(age.Value)
Else
Console.WriteLine("возраст не указан")
End If

Оператор If с тремя аргументами удобен для nullable:

Dim displayAge = If(age.HasValue, age.Value.ToString(), "н/д")

Практические правила

  1. Включайте Option Strict On в учебных и новых проектах.
  2. Не храните числа в Object «на всякий случай» — выбирайте конкретный тип или обобщение Of T.
  3. Для иерархии классов — TryCast + проверка на Nothing, для чисел — CType / CInt с пониманием округления.
  4. Деньги и координаты в бизнес-логике — Decimal / Structure, а не Double, если важна точность.

Что дальше

ТемаСтатья
Примитивы, Option StrictТипы
If, циклы5
Коллекции, файлы10
Архитектура .NET3

Что попробовать

  1. Скопируйте Structure в две переменные — измените поле в одной и сравните.
  2. Присвойте Nothing ссылочной переменной и проверьте Is Nothing.
  3. Используйте Integer? и HasValue для необязательного поля формы.

См. также

Другие статьи этого же раздела в боковом меню (как на странице «О разделе»).