Полное руководство по языку программирования С# 14 и платформе .NET 10 / Объектно-ориентированное программирование

Преобразование типов

Начальный ⏱ 6 мин чтения 👁 2 🕓 обновлено 11.08.2026

В предыдущей главе мы говорили о преобразованиях объектов простых типов. Сейчас затронем тему преобразования объектов классов. Допустим, у нас есть следующая иерархия классов:


class Person
{
	public string Name { get; set; }
	public Person(string name)
	{
		Name = name;
	}
	public void Print()
	{
		Console.WriteLine($"Person {Name}");
	}
}

class Employee : Person
{
	public string Company { get; set; }
	public Employee(string name, string company) : base(name)
	{
		Company = company;
	}
}

class Client : Person
{
	public string Bank { get; set; }
	public Client(string name, string bank) : base(name)
	{
		Bank = bank;
	}
}

В этой иерархии классов мы можем проследить следующую цепь наследования: Object (все классы неявно наследуются от типа Object) -> Person -> Employee|Client.

Причем в этой иерархии классов базовые типы находятся вверху, а производные типы - внизу.

Восходящие преобразования. Upcasting

Объекты производного типа (который находится внизу иерархии) в то же время представляют и базовый тип. Например, объект Employee в то же время является и объектом класса Person. Что в принципе естественно, так как каждый сотрудник (Employee) является человеком (Person). И мы можем написать, например, следующим образом:


Employee employee = new Employee("Tom", "Microsoft");
Person person = employee;   // преобразование от Employee к Person

Console.WriteLine(person.Name); 

В данном случае переменной person, которая представляет тип Person, присваивается ссылка на объект Employee. Но чтобы сохранить ссылку на объект одного класса в переменную другого класса, необходимо выполнить преобразование типов - в данном случае от типа Employee к типу Person. И так как Employee наследуется от класса Person, то автоматически выполняется неявное восходящее преобразование - преобразование к типу, которые находятся вверху иерархии классов, то есть к базовому классу.

В итоге переменные employee и person будут указывать на один и тот же объект в памяти, но переменной person будет доступна только та часть, которая представляет функционал типа Person.

Подобным образом поизводятся и другие восходящие преобразования:


Person bob = new Client("Bob", "ContosoBank");   // преобразование от Client к Person

Здесь переменная bob, которая представляет тип Person, хранит ссылку на объект Client, поэтому также выполняется восходящее неявное преобразование от производного класса Client к базовому типу Person.

Восходящее неявное преобразование будет происходить и в следующем случае:


object person1 = new Employee("Tom", "Microsoft");	// от Employee к object
object person2 = new Client("Bob", "ContosoBank");	// от Client к object
object person3 = new Person("Sam");					// от Person к object

Так как тип object - базовый для всех остальных типов, то преобразование к нему будет производиться автоматически.

Нисходящие преобразования. Downcasting

Но кроме восходящих преобразований от производного к базовому типу есть нисходящие преобразования или downcasting - от базового типа к производному. Например, в следующем коде переменная person хранит ссылку на объект Employee:


Employee employee = new Employee("Tom", "Microsoft");
Person person = employee;   // преобразование от Employee к Person

И может возникнуть вопрос, можно ли обратиться к функционалу типа Employee через переменную типа Person. Но автоматически такие преобразования не проходят, ведь не каждый человек (объект Person) является сотрудником предприятия (объектом Employee). И для нисходящего преобразования необходимо применить явное преобразование, указав в скобках тип, к которому нужно выполнить преобразование:


Employee employee1 = new Employee("Tom", "Microsoft");
Person person = employee1;   // преобразование от Employee к Person

//Employee employee2 = person;    // так нельзя, нужно явное преобразование
Employee employee2 = (Employee)person;  // преобразование от Person к Employee

Рассмотрим некоторые примеры преобразований:


// Объект Employee также представляет тип object
object obj = new Employee("Bill", "Microsoft");

// чтобы обратиться к возможностям типа Employee, приводим объект к типу Employee
Employee employee = (Employee) obj;

// объект Client также представляет тип Person
Person person = new Client("Sam", "ContosoBank");
// преобразование от типа Person к Client
Client client = (Client)person;

В первом случае переменной obj присвоена ссылка на объект Employee, поэтому мы можем преобразовать объект obj к любому типу который располагается в иерархии классов между типом object и Employee.

Если нам надо обратиться к каким-то отдельным свойствам или методам объекта, то нам необязательно присваивать преобразованный объект переменной:


// Объект Employee также представляет тип object
object obj = new Employee("Bill", "Microsoft");

// преобразование к типу Person для вызова метода Print
((Person)obj).Print();
// либо так
// ((Employee)obj).Print();

// преобразование к типу Employee, чтобы получить свойство Company
string company = ((Employee)obj).Company;

В то же время необходимо соблюдать осторожность при подобных преобразованиях. Например, что будет в следующем случае:


// Объект Employee также представляет тип object
object obj = new Employee("Bill", "Microsoft");

// преобразование к типу Client, чтобы получить свойство Bank
string bank = ((Client)obj).Bank;

В данном случае мы получим ошибку, так как переменная obj хранит ссылку на объект Employee. Данный объект является также объектом типов object и Person, поэтому мы можем преобразовать его к этим типам. Но к типу Client мы преобразовать не можем.

Другой пример:


Employee employee1 = new Person("Tom");	// ! Ошибка

Person person = new Person("Bob");
Employee employee2 = (Employee) person;	// ! Ошибка

В данном случае мы пытаемся преобразовать объект типа Person к типу Employee, а объект Person не является объектом Employee. Причем в последнем случае Visual Studio не подскжет, что в данной строке ошибка, и данная строка даже нормально скомилируется, тем не менее в процессе выполнения программы мы получим ощибку. В этом в том числе и кроектся коварство преобразований, поэтому в подобных ситуациях надо проявлять осторожность.

Существует ряд способов, чтобы избежать подобных ошибок преобразования.

Способы преобразований

Во-первых, можно использовать ключевое слово as. С помощью него программа пытается преобразовать выражение к определенному типу, при этом не выбрасывает исключение. В случае неудачного преобразования выражение будет содержать значение null:


Person person = new Person("Tom");
Employee? employee = person as Employee;
if (employee == null)
{
    Console.WriteLine("Преобразование прошло неудачно");
}
else
{
    Console.WriteLine(employee.Company);
}

Стоит отметить, что переменная employee здесь определяется не просто как переменная Employee, а именно Employee? - после названия типа ставится вопросительный знак. Что указывает, что переменная может хранить как значение null, так и значение Employee.

Второй способ заключается в проверке допустимости преобразования с помощью ключевого слова is:

значение is тип

Если значение слева от оператора представляет тип, указаный справа от оператора, то оператор is возвращает true, иначе возвращается false.

Причем оператор is позволяет автоматически преобразовать значение к типу, если это значение представляет данный тип. Например:


Person person = new Person("Tom");
if (person is Employee employee)
{
    Console.WriteLine(employee.Company);
}
else
{
    Console.WriteLine("Преобразование не допустимо");
}

Выражение if (person is Employee employee) проверяет, является ли переменная person объектом типа Employee. И если person является объектом Employee, то автоматически преобразует значение переменной person в тип Employee и преобразованное значение сохраняет в переменную employee. Далее в блоке if мы можем использовать объект employee как значение типа Employee.

Однако, если person не является объектом Employee, как в данном случае, то такая проверка вернет значение false, и преобразование не сработает.

Оператор is также можно применять и без преобразования, просто проверяя на соответствие типу:


Person person = new Person("Tom");
if (person is Employee)
{
    Console.WriteLine("Представляет тип Employee");
}
else
{
    Console.WriteLine("НЕ является объектом типа Employee");
}
Дополнительные материалы

Проверь себя

📋 Тест по теме «Преобразование типов» Тест по теме

Практические задания

Лёгкое Ручная проверка Заполните пропуск в коде (1)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

class _____
{
	public string Name { get; set; }
	public Person(string name)
	{
		Name = name;
	}
	public void Print()
	{
		Console.WriteLine($"Person {Name}");
	}
}

class Employee : Person
{
	public string Company { get; set; }
	public Employee(string name, string company) : base(name)
	{
		Company = company;
	}
}

class Client : Person
{
	public string Bank { get; set; }
	public Client(string name, string bank) : base(name)
	{
		Bank = bank;
	}
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

Employee _____ = new Employee("Tom", "Microsoft");
Person person = employee;   // преобразование от Employee к Person

Console.WriteLine(person.Name);
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

Person bob = _____ Client("Bob", "ContosoBank");   // преобразование от Client к Person
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

object person1 = new _____("Tom", "Microsoft");	// от Employee к object
object person2 = new Client("Bob", "ContosoBank");	// от Client к object
object person3 = new Person("Sam");					// от Person к object
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

Employee employee = new Employee("_____", "Microsoft");
Person person = employee;   // преобразование от Employee к Person
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

class _____
{
	public string Name { get; set; }
	public Person(string name)
	{
		Name = name;
	}
	public void Print()
	{
		Console.WriteLine($"Person {Name}");
	}
}

class _____ : Person
{
	public string Company { get; set; }
	public Employee(string name, string company) : base(name)
	{
		Company = company;
	}
}

class Client : Person
{
	public string Bank { get; set; }
	public Client(string name, string bank) : base(name)
	{
		Bank = bank;
	}
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

_____ employee = new Employee("Tom", "Microsoft");
Person _____ = employee;   // преобразование от Employee к Person

Console.WriteLine(person.Name);
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

Строки кода из примера этого урока перемешаны. Впишите их в правильном порядке, каждую строку — с новой строки.

Employee employee = new Employee("Tom", "Microsoft");
Console.WriteLine(person.Name);
Person person = employee;   // преобразование от Employee к Person
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Преобразование типов»
+30 XP

Опишите своими словами, что делает приведённый пример кода из урока «Преобразование типов», и предложите одно осмысленное изменение (например, другое входное значение или дополнительное условие). Приведите итоговый код.

class Person
{
	public string Name { get; set; }
	public Person(string name)
	{
		Name = name;
	}
	public void Print()
	{
		Console.WriteLine($"Person {Name}");
	}
}

class Employee : Person
{
	public string Company { get; set; }
	public Employee(string name, string company) : base(name)
	{
		Company = company;
	}
}

class Client : Person
{
	public string Bank { get; set; }
	public Client(string name, string bank) : base(name)
	{
		Bank = bank;
	}
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Преобразование типов»
+30 XP

Изучите приведённый фрагмент кода и опишите, при каких входных данных или условиях он может сработать не так, как ожидается (граничный случай, отсутствие проверки и т.п.). Предложите исправленный вариант.

Employee employee = new Employee("Tom", "Microsoft");
Person person = employee;   // преобразование от Employee к Person

Console.WriteLine(person.Name);
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Преобразование типов»
+50 XP

Соберите небольшую программу, которая последовательно демонстрирует ключевые идеи урока. Программа должна быть рабочей и содержать минимум два самостоятельно написанных фрагмента, а не просто копию примера из урока.

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «Преобразование типов»
+25 XP

Напишите собственную небольшую программу, применяющую материал темы «Преобразование типов». Используйте не менее одной конструкции, показанной в примерах урока. Прикрепите код решения и краткое описание того, что он делает.

Войдите, чтобы отправить решение

Обсуждение (0)

Войдите, чтобы оставить комментарий.