Share This
Связаться со мной
Крути в низ
Categories
//❓ Как использовать обобщения в C# – краткая инструкция для новичков

❓ Как использовать обобщения в C# – краткая инструкция для новичков

Обобщения (generics) необходимы в тех случаях, когда мы не можем заранее знать тип данных, который будем использовать. Они есть во многих языках программирования, и сегодня мы расскажем про обобщения в C# – материал будет полезен для начинающих разработчиков.

kak ispolzovat obobshhenija v c kratkaja instrukcija dlja novichkov cfc419a - ❓ Как использовать обобщения в C# – краткая инструкция для новичков

Термины: Обобщения, обобщённые типы, шаблоны, дженерики и generics обозначают одно и то же.

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

         class User     {  public int  Id { get; set; }  public string Name { get; set; } }     

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

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

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

         class User {  public Object  Id { get; set; }  public string Name { get; set; } }     

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

         User userOne = new User{}; User userTwo = new User{};  userOne.Id = 1; userTwo.Id = "2";  int OneID = (int)userOne.Id; string TwoID = (string)userTwo.Id;  Console.WriteLine(OneID); Console.WriteLine(TwoID);     

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

         userOne.Id = 1; //Упаковка типа int в тип Object     

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

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

         int OneID = (int)userOne.Id; //Распаковка типа Object в тип int     

Распаковка – это явное преобразование значения типа Object к некоторому другому типу. Подробнее про процессы упаковки и распаковки можно почитать в руководстве по программированию на C#.

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

         userTwo.Id = "2"; int TwoID = (int)userTwo.Id; // Исключение InvalidCastException     

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

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

Синтаксис:

         class ClassName<T> {  public T PropertyName { get; set; } }      

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

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

         class User<T> {  private T session; //Шаблон применяется к полю  public T  Id { get; set; } //Шаблон применяется к свойству  public string Name { get; set; } }     

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

         //Треугольные скобки после имени класса указывают на то, //что внутри класса будет использоваться тип int  User<int> userOne = new User<int> { };   //Треугольные скобки после имени класса указывают на то, //что внутри класса будет использоваться тип string  User<string> userTwo = new User<string> { };  userOne.Id = 1; userTwo.Id = "2";  int OneID = userOne.Id;  //При попытке присвоить переменной типа int значение типа string  //компилятор выдаст ошибку int TwoID = userTwo.Id;  Console.WriteLine(OneID); Console.WriteLine(TwoID);     

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

         class User<T, U> {  private T session; //Шаблон применяется к полю  public T  Id { get; set; } //Шаблон применяется к свойству  public U Name { get; set; } }     

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

         public void DoAction<T>(T ValueOne, T ValueTwo) {};     

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

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

         class UserList<T> where T: User<int> {  T [] List = new T[10]; }     

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

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

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

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

  1. Название класса, class или struct. Эти ограничения нельзя применять одновременно.
  2. Название интерфейса.
  3. new().
         interface IUser { }  class User { }   class UserList<T> where T: User , IUser , new() { }      

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

         class UserList<T, U, V>   where T: User   where U: new() {}      

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

         public static void DoAction<T>(T ValueOne, T ValueTwo) where T : User {}     

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

***

Обобщения присутствует во многих языках программирования. Шаблонизация используется фактически во всех динамических структурах языка C# (списки, очереди, стеки и т. д). Знание этого инструмента программирование поможет вам создавать более универсальные структуры и алгоритмы. Удачи в обучении!

  • 9 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close