Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
{
if (obj is Car temp)
{
return this.CarID.CompareTo(temp.CarID);
}
throw new ArgumentException("Parameter is not a Car!");
// Параметр не является объектом типа Car!
}
}
Теперь представим, что применяется обобщенный аналог данного интерфейса:
public interface IComparable<T>
{
int CompareTo(T obj);
}
В таком случае код реализации будет значительно яснее:
public class Car : IComparable<Car>
{
...
// Реализация IComparable<T>.
int IComparable<Car>.CompareTo(Car obj)
{
if (this.CarID > obj.CarID)
{
return 1;
}
if (this.CarID < obj.CarID)
{
return -1;
}
return 0;
}
}
Здесь уже не нужно проверять, относится ли входной параметр к типу Car, потому что он может быть только Car! В случае передачи несовместимого типа данных возникает ошибка на этапе компиляции. Теперь, углубив понимание того, как взаимодействовать с обобщенными элементами, а также усвоив роль параметров типа (т.е. заполнителей), вы готовы к исследованию классов и интерфейсов из пространства имен System.Collections.Generic.
Пространство имен System.Collections.Generic
Когда вы строите приложение .NET Core и необходим способ управления данными в памяти, классы из пространства имен System.Collections.Generic вероятно удовлетворят всем требованиям. В начале настоящей главы кратко упоминались некоторые основные необобщенные интерфейсы, реализуемые необобщенными классами коллекций. Не должен вызывать удивление тот факт, что в пространстве имен System.Collections.Generic для многих из них определены обобщенные замены.
В действительности вы сможете найти некоторое количество обобщенных интерфейсов, которые расширяют свои необобщенные аналоги, что может показаться странным.Тем не менее, за счет этого реализующие их классы будут также поддерживать унаследованную функциональность, которая имеется в их необобщенных родственных версиях. Например, интерфейс IEnumerable<T> расширяет IEnumerable. В табл. 10.4 описаны основные обобщенные интерфейсы, с которыми вы столкнетесь во время работы с обобщенными классами коллекций.
В пространстве имен System.Collections.Generic также определены классы, реализующие многие из указанных основных интерфейсов. В табл. 10.5 описаны часто используемые классы из этого пространства имен, реализуемые ими интерфейсы, а также их базовая функциональность.
В пространстве имен System.Collections.Generic также определены многие вспомогательные классы и структуры, которые работают в сочетании со специфическим контейнером. Например, тип LinkedListNode<T> представляет узел внутри обобщенного контейнера LinkedList<T>, исключение KeyNotFoundException генерируется при попытке получения элемента из коллекции с применением несуществующего ключа и т.д. Подробные сведения о пространстве имен System.Collections.Generic доступны в документации по .NET Core.
В любом случае следующая ваша задача состоит в том, чтобы научиться использовать некоторые из упомянутых классов обобщенных коллекций. Тем не менее, сначала полезно ознакомиться со средством языка C# (введенным в версии .NET 3.5), которое упрощает заполнение данными обобщенных (и необобщенных) коллекций.
Синтаксис инициализации коллекций
В главе 4 вы узнали о синтаксисе инициализации массивов, который позволяет устанавливать элементы новой переменной массива во время ее создания. С ним тесно связан синтаксис инициализации коллекций. Данное средство языка C# позволяет наполнять многие контейнеры (такие как ArrayList или List<T>) элементами с применением синтаксиса, похожего на тот, который используется для наполнения базовых массивов. Создайте новый проект консольного приложения .NET Core по имени FunWithCollectionInitialization. Удалите код, сгенерированный в Program.cs, и добавьте следующие операторы using:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
На заметку! Синтаксис инициализации коллекций может применяться только к классам, которые поддерживают метод Add(), формально определяемый интерфейсами ICollection<T> и ICollection.
Взгляните на приведенные ниже примеры:
// Инициализация стандартного массива.
int[] myArrayOfInts = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// Инициализация обобщенного List<> с элементами int.
List<int> myGenericList = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// Инициализация ArrayList числовыми данными.
ArrayList myList = new ArrayList { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Если контейнером является коллекция классов или структур, тогда синтаксис инициализации коллекций можно смешивать с синтаксисом инициализации объектов, получая функциональный код. Вспомните класс Point из главы 5, в котором были определены два свойства, X и Y. Для построения обобщенного списка List<T> объектов Point можно написать такой код:
List<Point> myListOfPoints = new List<Point>
{
new Point { X = 2, Y = 2 },
new Point { X = 3, Y = 3 },
new Point { X = 4, Y = 4 }
};
foreach (var pt in myListOfPoints)
{
Console.WriteLine(pt);
}
Преимущество этого синтаксиса связано с сокращением объема клавиатурного ввода. Хотя вложенные фигурные скобки могут затруднять чтение кода, если не позаботиться о надлежащем форматировании, вы только вообразите себе объем кода, который пришлось бы написать для наполнения следующего списка List<T> объектов Rectangle без использования синтаксиса инициализации коллекций:
List<Rectangle> myListOfRects = new List<Rectangle>
{
new Rectangle {
Height = 90, Width = 90,
Location = new Point { X = 10, Y = 10 }},
new Rectangle {
Height = 50,Width = 50,
Location = new Point { X = 2, Y = 2 }},
};
foreach (var r in myListOfRects)
{
Console.WriteLine(r);
}
Работа с классом List<T>
Создайте новый проект консольного приложения под названием FunWithGenericCollections. Добавьте новый файл по имени Person.cs и поместите в него показанный ниже код (это тот же самый код с определением предыдущего класса Person):
namespace FunWithGenericCollections
{
public class Person
{
public int Age {get; set;}
public string