Выражение должно иметь тип класса c windows forms

Обновлено: 04.07.2024

Этот урок будет посвящен тому, как мы можем создавать приложения для Windows. Мы также изучим некоторые основы работы с различными элементами приложений Windows.

В этом уроке вы узнаете

Основы Windows Forms

Приложение Windows Forms работает на настольном компьютере. Приложение форм Windows обычно имеет набор элементов управления, таких как метки, текстовые поля, списки и т. Д.

Итак, пример элементов управления, доступных в приведенном выше приложении

Теперь давайте рассмотрим пример того, как мы можем реализовать простое приложение «Hello World» в Visual Studio. Для этого нам нужно будет выполнить следующие шаги

Шаг 1) Первый шаг включает создание нового проекта в Visual Studio. После запуска Visual Studio вам нужно выбрать пункт меню New-> Project.

Если вышеуказанные шаги будут выполнены, вы получите следующий вывод в Visual Studio.

Вывод:-

Вы увидите конструктор форм, отображаемый в Visual Studio. Именно в этом конструкторе форм вы начнете создавать приложение Windows Forms.

В обозревателе решений вы также сможете увидеть решение DemoApplication. Это решение будет содержать ниже 2 файла проекта

  1. Приложение Form называется Forms1.cs. Этот файл будет содержать весь код приложения Windows Form.
  2. Основная программа с именем Program.cs является файлом кода по умолчанию, который создается при создании нового приложения в Visual Studio. Этот код будет содержать код запуска приложения в целом.

В левой части Visual Studio вы также увидите панель инструментов. Панель инструментов содержит все элементы управления, которые можно добавить в Windows Forms. Элементы управления, такие как текстовое поле или метка, являются лишь некоторыми из элементов управления, которые можно добавить в Windows Forms.

Ниже приведен скриншот того, как выглядит Панель инструментов.

Шаг 3) На этом этапе мы добавим в форму метку, которая будет отображать «Hello World». На панели инструментов вам нужно будет выбрать элемент управления Label и просто перетащить его на форму.

После того, как вы перетащите метку на форму, вы можете увидеть метку, встроенную в форму, как показано ниже.

Чтобы перейти к свойствам элемента управления, необходимо щелкнуть его правой кнопкой мыши и выбрать пункт меню «Свойства».

  • Панель свойств также отображается в Visual Studio. Таким образом, для элемента управления меткой в ​​элементе управления свойствами перейдите в раздел «Текст» и введите «Hello World».
  • Каждый элемент управления имеет набор свойств, которые описывают элемент управления.

Если вы выполните все вышеперечисленные шаги и запустите свою программу в Visual Studio, вы получите следующий вывод

Вывод:-

В выводе вы можете видеть, что форма Windows отображается. Вы также можете увидеть «Hello World» отображается в форме.

Добавление элементов управления в форму

Мы уже видели, как добавить элемент управления в форму, когда добавили элемент управления меткой в ​​предыдущем разделе, чтобы отобразить «Hello World».

Давайте посмотрим на другие элементы управления, доступные для форм Windows, и рассмотрим некоторые их общие свойства.

В нашем примере мы создадим одну форму, которая будет иметь следующую функциональность.

Итак, давайте подробно рассмотрим каждый элемент управления и добавим их для построения формы с вышеупомянутой функциональностью.

Групповая коробка

Групповое поле используется для логической группировки элементов управления в раздел. Давайте рассмотрим пример, если у вас была коллекция элементов управления для ввода таких данных, как имя и адрес человека. В идеале это данные человека, поэтому вы бы хотели, чтобы эти данные были указаны в отдельном разделе формы. Для этого у вас может быть групповое поле. Давайте посмотрим, как мы можем реализовать это на примере, показанном ниже

Шаг 2) После того, как группа будет добавлена, перейдите в окно свойств, нажав на элемент управления группы. В окне свойств перейдите к свойству Text и измените его на «Сведения о пользователе».

После внесения вышеуказанных изменений вы увидите следующий вывод

Вывод:-

В выводе вы можете четко видеть, что Groupbox был добавлен в форму. Вы также можете видеть, что текст группового блока был изменен на «Сведения о пользователе».

Контроль над этикетками

Шаг 2) После добавления метки перейдите в окно свойств, щелкнув элемент управления меткой. В окне свойств перейдите к свойству Text каждого элемента управления label.

После внесения вышеуказанных изменений вы увидите следующий вывод

Вывод:-

Вы можете видеть элементы управления надписью, добавленные в форму.

Текстовое окно

Текстовое поле используется для того, чтобы пользователь мог ввести текст в приложение форм. Давайте посмотрим, как мы можем реализовать это на примере, показанном ниже. Мы добавим в форму 2 текстовых поля, одно для Имени и другое для адреса, который будет введен для пользователя.

После внесения вышеуказанных изменений вы увидите следующий вывод

Вывод:-

На выходе вы можете четко видеть, что текстовые поля были добавлены в форму.

Список

Список используется для отображения списка элементов в форме Windows. Давайте посмотрим, как мы можем реализовать это на примере, показанном ниже. Мы добавим в форму список для хранения некоторых городов.

Шаг 2) После того, как список был добавлен, перейдите в окно свойств, нажав на элемент управления списка.

  1. Сначала измените свойство элемента управления Listbox, в нашем случае мы изменили его на lstCity
  2. Нажмите на свойство Items. Это позволит вам добавлять различные элементы, которые могут отображаться в списке. В нашем случае мы выбрали элементы «коллекция».
  3. В появившемся всплывающем редакторе коллекции строк введите названия городов. В нашем случае мы ввели «Мумбаи», «Бангалор» и «Хайдарабад».
  4. Наконец, нажмите кнопку «ОК».

После внесения вышеуказанных изменений вы увидите следующий вывод

Вывод:-

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

Переключатель

Radiobutton используется для демонстрации списка элементов, из которых пользователь может выбрать один. Давайте посмотрим, как мы можем реализовать это на примере, показанном ниже. Мы добавим переключатель для мужской / женской опции.

Шаг 2) После добавления радиокнопки перейдите в окно свойств, щелкнув элемент управления радиокнопки.

  1. Во-первых, вам нужно изменить свойство текста обоих элементов управления Radio. Перейдите в окно свойств и измените текст на мужской радиобутон, а на другой женский.
  2. Аналогичным образом измените свойство name обоих элементов управления Radio. Перейдите в окно свойств и измените имя на «rdMale» одной радиокнопки и на «rdfemale» для другой.

Как только вы сделаете вышеуказанные изменения, вы увидите следующий вывод

Вывод:-

Вы увидите переключатели, добавленные в форму Windows.

флажок

Шаг 2) После того, как флажок был добавлен, перейдите в окно свойств, нажав на элемент управления флажок.

После внесения вышеуказанных изменений вы увидите следующий вывод

Вывод:-

кнопка

Шаг 2) После добавления кнопки перейдите в окно свойств, нажав на элемент управления Button.

После внесения вышеуказанных изменений вы увидите следующий вывод

Вывод:-

Поздравляю, теперь у вас есть первая базовая форма Windows. Давайте теперь перейдем к следующей теме, чтобы увидеть, как мы можем выполнять обработку событий для элементов управления.

Обычно, когда кнопка нажимается на форме, это означает, что должна выполняться некоторая обработка.

Давайте посмотрим на одно из событий и как оно может быть обработано, прежде чем мы перейдем к сценарию события кнопки.

Шаг 1) Дважды щелкните по списку в конструкторе форм . Делая это, Visual Studio автоматически откроет файл кода для формы. И он автоматически добавит метод события в код. Этот метод события будет запускаться всякий раз, когда выбран какой-либо элемент в списке.

Выше приведен фрагмент кода, который автоматически добавляется Visual Studio при двойном щелчке элемента управления «Список» на форме. Теперь давайте добавим приведенный ниже фрагмент кода в этот фрагмент кода, чтобы добавить необходимые функции в событие списка.

Как только вы внесете вышеуказанные изменения и запустите программу в Visual Studio, вы увидите следующий вывод

Вывод:-

Теперь давайте посмотрим на последний элемент управления, который представляет собой метод нажатия кнопки. Опять же, это следует той же философии. Просто дважды щелкните кнопку в конструкторе форм, и он автоматически добавит метод для обработчика события кнопки. Тогда вам просто нужно добавить код ниже.

  1. Это метод обработчика событий, который автоматически создается Visual Studio при двойном щелчке по кнопке управления. Вам не нужно беспокоиться о сложности имени метода или параметров, передаваемых в метод.
  2. Здесь мы получаем значения, введенные в текстовое поле имени и адреса. Значения могут быть взяты из свойства text текстового поля. Затем мы присваиваем значения двум переменным: name и address соответственно.
  3. Наконец, мы используем метод MessageBox для отображения значения имени и адреса для пользователя.

Как только вы внесете вышеуказанные изменения и запустите программу в Visual Studio, вы увидите следующий вывод

Вывод:-

Управление Tree и PictureBox

Управление Деревом

Давайте посмотрим, как мы можем реализовать это на примере, показанном ниже.

  1. Перейдите к панели инструментов свойств для древовидного элемента управления. Нажмите на свойство узла. Это вызовет редактор TreeNode
  2. В редакторе TreeNode нажмите кнопку Add Root, чтобы добавить корневой узел в коллекцию дерева.
  3. Затем измените текст корневого узла, укажите текст в качестве корневого и нажмите кнопку «ОК». Это добавит корневой узел.
  1. Сначала нажмите кнопку «Добавить дочерний элемент». Это позволит вам добавить дочерние узлы в коллекцию Tree.
  2. Для каждого дочернего узла измените свойство text. Продолжайте повторять предыдущий шаг и этот шаг и добавьте 2 дополнительных узла. В итоге у вас будет 3 узла, как показано выше, с текстом соответственно Label, Button и Checkbox.
  3. Нажмите на кнопку ОК

После внесения вышеуказанных изменений вы увидите следующий вывод.

Вывод:-

Вы сможете увидеть древовидное представление, добавленное к форме. Когда вы запускаете приложение формы Windows, вы можете развернуть корневой узел и увидеть дочерние узлы в списке.

PictureBox Control

Этот элемент управления используется для добавления изображений в Windows Forms. Давайте посмотрим, как мы можем реализовать это на примере, показанном ниже.

  1. Сначала нажмите на свойство Image для элемента управления PictureBox. Появится новое окно.
  2. В этом окне нажмите на кнопку «Импорт». Это будет использоваться для прикрепления изображения к элементу управления Picturebox.
  3. Появится диалоговое окно, в котором вы сможете выбрать изображение для прикрепления картинки
  4. Нажмите на кнопку ОК

Как только вы сделаете вышеуказанные изменения, вы увидите следующий вывод

Зачем использовать ограничения

Ограничения определяют возможности и ожидания параметра типа. Объявление этих ограничений означает, что можно использовать операции и вызовы методов ограничивающего типа. Если универсальный класс или метод использует любые другие операции с универсальными элементами, помимо простого присвоения или вызова методов, которые не поддерживает System.Object, вам необходимо будет применить ограничения к параметру типа. Например, на основе ограничения базового класса компилятор определяет, что в качестве аргументов типа будут использоваться только объекты указанного типа или производных от него. Имея такую гарантию, компилятор может вызывать методы указанного типа в универсальном классе. В следующем примере кода показаны функциональные возможности, которые можно добавить в класс GenericList<T> (см. раздел Введение в универсальные шаблоны), применив ограничение базового класса.

Это ограничение позволяет универсальному классу использовать свойство Employee.Name . Ограничение указывает, что все элементы типа T гарантированно являются либо объектом Employee , либо объектом, который наследует от Employee .

К одному параметру типа можно применять несколько ограничений, которые сами по себе могут быть универсальными типами, как показано ниже:

При применении ограничения where T : class не рекомендуется использовать операторы == и != для параметра типа, поскольку в этом случае будет проверяться только удостоверение ссылки, а не равенство значений. Такое поведение будет наблюдаться даже в том случае, если эти операторы будут перегружены в типе, используемом в качестве аргумента. Эта особенность показана в следующем коде, который будет возвращать значение false даже в том случае, если класс String перегружает оператор == .

Компилятору известно только то, что T является ссылочным типом во время компиляции, и он должен использовать операторы по умолчанию, которые действительны для всех ссылочных типов. Чтобы проверить равенство значений, рекомендуется применить ограничение where T : IEquatable<T> или where T : IComparable<T> и реализовать этот интерфейс в любом классе, который будет использоваться для создания универсального класса.

Ограничение нескольких параметров

Ограничения можно применить к нескольким параметрам. Кроме того, к одному параметру можно применять несколько ограничений, как показано в следующем примере:

Несвязанные параметры типа

Не имеющие ограничений параметры типа (например, T в общем классе SampleClass<T><> ) называются несвязанными. В отношении несвязанных параметров типа применяются следующие правила:

  • Не допускается использование операторов != и == , поскольку нет гарантии, что они будут поддерживаться конкретным аргументом типа.
  • Их можно преобразовать в System.Object или явно преобразовать в любой тип интерфейса.
  • Можно сравнить их со значением null. При сравнении несвязанного параметра с null для аргументов типа, являющихся типами значений, всегда возвращается значение false.

Параметры типа в качестве ограничений

Использование параметров универсального типа в качестве ограничений применимо, когда функция-член со своим параметром типа должна ограничивать этот параметр параметром содержащего типа, как показано в следующем примере:

В предыдущем примере T является ограничением типа в контексте метода Add и несвязанным параметром типа в контексте класса List .

Параметры типа также можно использовать в определениях универсальных классов. Параметр типа необходимо объявлять в угловых скобках вместе с любыми другими параметрами типа:

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

Ограничение notnull

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

Ограничение class

Ограничение default

Так как T? теперь можно использовать без ограничения class или struct , в переопределениях или явных реализациях интерфейса могут возникать неоднозначности. В обоих случаях переопределение не включает в себя ограничения, но наследует их от базового класса. Если базовый класс не применяет ограничение class или struct , производные классы должны каким-либо образом указывать переопределение, применяемое к базовому методу без ограничения. Это происходит, когда производный метод применяет ограничение default . Ограничение default не уточняет ни ограничение class , ни struct .

Неуправляемое ограничение

В примере выше метод необходимо компилировать в контексте unsafe , так как он использует оператор sizeof для типа, не известного как встроенный тип. Без ограничения unmanaged оператор sizeof недоступен.

Ограничение unmanaged подразумевает ограничение struct и не может использоваться совместно с ним. Поскольку ограничение struct подразумевает ограничение new() , ограничение unmanaged также не может использоваться с ограничением new() .

Ограничения делегата

Приведенный выше метод можно использовать для объединения делегатов, которые относятся к одному типу:

Если раскомментировать последнюю строку, она не будет компилироваться. first и test являются типами делегатов, но это разные типы делегатов.

Ограничения перечисления

Enum.GetValues и Enum.GetName используют отражение, которое влияет на производительность. Вы можете не повторять вызовы, требующие отражения, а вызвать EnumNamedValues для создания коллекции, которая кэшируется и используется повторно.

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

Исходный код примеров из этой статьи можете скачать из нашего github-репозитория.

Языки программирования и типы данных

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

  • со статической и динамической типизацией;
  • с сильной и слабой типизацией;
  • с явной и неявной типизацией.

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

Примеры языков с динамической типизацией: Python , PHP . Пример кода на Python :

В языках с сильной типизацией операции над значениями и присваивания можно производить только над переменными одного типа. Иногда это приведение выполняется автоматически, например:

В этом случае, при выполнении второй строки в первую очередь будет выполнено приведение переменной v1 к типу double , а потом сложение. Но следующий код вызовет ошибку:

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

В результате код скомпилируется, если его запустить, то в переменной double будет лежать численное значение.

Явная типизация предполагает явное указание типа переменной:

В этом примере, мы объявляем переменную value типа int и явно это указываем.

В языке с неявной типизацией этого делать не нужно, пример на Python :

Общая система типов (CTS)

Объявление и инициализация переменных

Задание значения переменной можно произвести в момент инициализации:

либо после инициализаций:

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

В примерах мы не будем приводить код импорта и объявления класса. В конце главы будет приведен листинг программы со всеми примерами из данного урока.

Ключевое слово new

Ключевое слово new , как правило, используется при инициализации переменных, которые имеют ссылочный тип данных. О том, что это такое мы расскажем чуть ниже. Пусть у нас есть класс Rectangle :

Данный класс нам нужен только для демонстрации, при разработке собственных классов не стоит создать поля с ключевым словом public . О создании классов и основах объектно-ориентированного программирования будет рассказано в одном из ближайших уроков.

Создадим переменную класса Rectangle :

Переменные типа int , double и т.п. также можно проинициализировать с помощью ключевого слова new , в этом случае будет присвоено значение по умолчанию:

Ключевое слово var. Неявная типизация

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

При работе с var необходимо помнить следующее:

  • использовать var можно только для объявления локальных переменных;
  • var нельзя использоваться для объявления типа возвращаемого значения, типов полей и параметров;
  • при объявлении переменной с использованием var она обязательно должна быть проинициализирована, при этом использовать для этого null запрещено;
  • объявлять переменную допускающую null -значение с использованием лексемы ? через var нельзя.


Типы значения

Переменные типа-значения располагаются в стеке, что позволяет их быстро создавать и уничтожать. Фактически время жизни такой переменной определяется контекстом, в которой она объявлена. Сама переменная представляется в виде локальной копии. Типы-значения являются классами наследниками от System.ValueType , который, в свою очередь, наследуется от System.Object . К типам-значениям относятся: простые типы, типы перечисления, типы структур, типы значений, допускающие NULL , типы значений кортежей. Далее, обзорно будут рассмотрены указанные выше типы.

Простые типы

К простым типа относятся:

  • Целочисленные типы;
  • Типы с плавающей точкой;
  • Тип bool для представления логических значений;
  • Тип char для представления символьных значений.

Целочисленные типы

Примеры работы с целыми числами:

Для явного указания, что число имеет тип long или ulong необходимо добавить суффикс L или l для long и UL и все возможные комбинации регистров эти символов для ulong .

Число может быть представлено в десятичном, шестнадцатеричном и двоичном виде:

Для всех целочисленных простых типов значение по умолчанию: 0 , оно присваивается переменной при инициализации с помощью ключевого слова new .

Типы с плавающей точкой

Сводная таблица с типами с плавающей точкой:

Для явного указания типа данных при записи числа, можно использовать литералы:

Для чисел с плавающей точкой значение по умолчанию: 0.0 с соответствующей литерой в конце.

Тип bool

Значение по умолчанию для типа bool : false .

Тип char

Переменной типа char можно задать значение:

  • в виде символа:
  • escape -последовательности Юникода (начинаются с префикса \u ):
  • шестнадцатеричной escape -последовательности (начинается с префикса \x ):
  • через приведение типа:

Типы перечисления (enum)

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

Каждому значению перечисления соответствует целое число.

Пример создания переменной типа Day :

Более подробно про работу с enum будет рассказано в одном из следующих уроков.

Типы структур

Структуры по своей внутренней организации похожи на классы, они содержат набор полей и методов. Как правило, их используют для объявления типов, которые определяются только значениями полей и не имеют индивидуальности. Например, объекты, описывающие транзакции, несмотря на то, что значения их полей могут совпадать не будут тождественными, то есть нам их нужно уметь различать несмотря на внешнее сходство. А точки на геометрической плоскости, которые задаются двумя координатами, такой индивидуальности не имеют, и если координаты двух точек совпадают, то это значит, что речь идет об одной и той же точке. Именно для таких типов хорошо подходят структуры. Для их объявления используется ключевое слово struct :

Типы значений, допускающие null

Про типы значений, допускающих null см. ниже “ Nullable -типы (нулевые типы) и операция ??” .

Типы значений кортежей

Кортежи используются для группировки данных, которые могут иметь разные типы в единую именованную сущность. Они являются объектами типа System.ValueTuple . Объявим кортеж, состоящий из двух элементов типа double:

Поля кортежа могут быть именованными:

Более подробно про кортежи типов System.ValueTuple (тип-значение) и System.Tuple (ссылочный тип) будет рассказано в одном из следующих уроков.

Ссылочные типы

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

Типы классов

Типы интерфейсов

Создадим интерфейс для описания человека, у которого есть два свойства имя: Name , и возраст: Age :

Изменим объявление класса Persone, так, чтобы он представлял реализацию интерфейса IPersone:

Объявим переменную типа IPersone:

Более подробно про интерфейсы будет рассказано в одном из следующих уроков.

Типы массивов

Создание и инициализация одномерного массива:

Пример прямоугольного массива, в нем строки имеют одинаковую длину:

Пример зубчатого ( jagged ) массива, в нем строки могут иметь разную длину:

Более подробно про массивы будет рассказано в одном из следующих уроков.

Типы делегатов

Делегаты являются аналогом указателей на функции из языков C / C++ . Они используются в случаях, когда нужно передать некоторую функциональность как аргумент, перенаправлять вызовы и т.д.

Nullable-типы (нулевые типы) и операция ??

Объявление и инициализация Nullable-переменных

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

На практике, особенно при работе с базами данных, может возникнуть ситуация, когда в записи из таблицы пропущены несколько столбцов (нет данных), в этом случае, соответствующей переменной нужно будет присвоить значение null , но она может иметь тип int или double , что приведет к ошибке.

Проверка на null. Работа с HasValue и Value

Для того чтобы проверить, что переменная имеет значение null можно воспользоваться оператором is с шаблоном типа:

Также можно воспользоваться свойствами класса Nullable :

  • Nullable<T>.HasValue
    • Возвращает true если переменная имеет значение базового типа. То есть если она не null .
    • Возвращает значение переменной если HasValue равно true , иначе выбрасывает исключение InvalidOperationException .

    Приведение Nullable-переменной к базовому типу

    При работе с Nullable -переменными их нельзя напрямую присваивать переменным базового типа. Следующий код не будет скомпилирован:

    Для приведения Nullable -переменной к базовому типу можно воспользоваться явным приведением:

    В этом случае следует помнить, что если значение Nullable -переменной равно null , то при выполнении данной операции будет выброшено исключение InvalidOperationException .

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

    Ключевое слово dynamic

    Ниже приведены несколько примеров, на которых можно разобраться с тем, как работать с dynamic :

    Как вы можете видеть значение и тип переменной dval1 менялись в процессе выполнения программы. При этом нужно помнить, что если вы присвоили переменной dynamic , какое-то значение, которое определило ее тип, а пытаетесь с ней работать как с переменной другого типа, то будет вызвано исключение:

    Оператор default

    Объявим переменную типа int и присвоим ей значение по умолчанию с помощью new :

    Тоже самое можно сделать с помощью оператора default :

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

    Вызовем эту функцию:

    Исходный код примеров из этой статьи можете скачать из нашего github-репозитория.


    Предположим, мы пишем приложение, в котором необходимо работать с данными пользователя:

    У класса User есть два свойства: ID – уникальный идентификатор и Name – имя пользователя. В данном случае идентификатор имеет тип данных int (целочисленный). Однако ID может быть типом string или GUID. Чтобы это предусмотреть, можно пойти одним из путей:

    • Написать много реализаций класса под каждый тип данных.
    • Использовать тип Object как универсальный тип данных.
    • Использовать обобщённые типы.

    Первый вариант не подходит из-за избыточности кода. Разберём второй:

    Теперь используем этот класс:

    Код отработает без ошибок, но в нем есть подводные камни. При присвоении свойству Id целочисленного значения происходит процесс упаковки (boxing):

    Упаковка – это неявное преобразование значения некоторого типа (например, int) к типу Object.

    При получении данных обратно в переменную типа int выполняется процесс распаковки (unboxing).

    Упаковка и распаковка ведут к снижению производительности, так как требуют затрат ресурсов системы на преобразование типов. Кроме того, такой способ не является безопасным (type safety). Если в переменную (свойство объекта) типа Object упакована строка (string), то при попытке ее распаковки в int мы получим исключение InvalidCastException.

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

    Обобщение типа

    Литера T (от англ. template — шаблон) в угловых скобках означает, что будет использоваться обобщение. Вместо символа T может быть выбрано любое наименование обобщения, однако такое написание считается общепринятым. Параметр в угловых скобках называется универсальным параметром.

    Перепишем класс с использованием шаблона:

    При создании экземпляра класса вместо буквы Т нужно указать тот тип данных, который нам необходим:

    Допускается использование несколько универсальных параметров:

    Обобщения также могут быть применены к интерфейсам, классам, структурам, методам и делегатам.

    Ограничения универсального параметра

    Выражение where T: User говорит о том, что универсальный параметр T должен быть представлен классом User , или его наследником. В качестве ограничения может применяться только один класс.

    Ограничением могут быть следующие типы:

    • классы;
    • интерфейсы;
    • class – универсальный параметр должен быть классом;
    • struct – универсальный параметр должен быть структурой;
    • new() – универсальный параметр должен иметь общедоступный (public) конструктор без параметров.

    При использовании нескольких ограничений, задать их можно в строгом порядке:

    1. Название класса, class или struct. Эти ограничения нельзя применять одновременно.
    2. Название интерфейса.
    3. new().

    Можно задать ограничения нескольким универсальным параметрам:

    Таким же способом можно задать ограничение обобщённых методов:

    Универсальный тип метода DoAction ограничен классом User или его наследником.

    Читайте также: