Методы преобразования изменяют тип входных объектов. Эти методы LINQ можно использовать, например, для того, чтобы скрыть реализацию коллекции или для того, чтобы запрос LINQ был выполнен немедленно.
AsEnumerable
Этот метод возвращает входное значение, типизированное как IEnumerable<T> и может использоваться для сокрытия реализации какой-либо коллекции. Например, у нас есть следующий список:
List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
мы можем выполнять любые операции с этим списком, которые он поддерживает, например, получить количество элементов в списке или получить конкретный элемент по его индексу:
Console.WriteLine($"Количество элементов в списке {list.Count}");
for (int i=0; i<list.Count; i++)
Console.WriteLine(list[i]);
Если мы выполним метод AsEnumerable для этого списка, то, в итоге сможем проводить только те операции со списком, которые доступны через интерфейс IEnumarable, т.е. мы скроем конкретную реализацию нашей коллекции:
var data = list.AsEnumerable();
Console.WriteLine($"Количество элементов в списке {data.Count()}");
var enumerator = data.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}
Метод AsEnumarable может пригодиться в том случае, если не требуется использование каких-либо специфических действий с коллекцией. Например, если у вас нет необходимости получать элемент коллекции по его индексу (как у List<T>), а достаточно просто перечислить все элементы или же полученная коллекция из одного запроса LINQ будет использоваться в другом запросе — в этом случае нет никакой необходимости преобразовывать коллекцию каким-то другим более «тяжелым» типам данных.
AsQueryable
Возвращает входное значение, типизированное как IQueryable, который используется для запросов данных.
var query = list.AsQueryable(); Console.WriteLine(query.ElementType.Name); Console.WriteLine(query.Provider.ToString());
Cast
Приводит элементы коллекции к указанному типу и возвращает объект типа IEnumerable<T>. Единственными преобразованиями типов, выполняемыми этим методом, являются преобразования ссылок и преобразования распаковки (unboxing). Например,
List<object> list = new List<object>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
var lst = list.Cast<int>().ToList();
В данном случае все числа, добавляемые в список будут упакованы в object. Для распаковки мы использовали метод Cast. Если же мы попытаемся преобразовать все элементы списка в тип string вместо int:
List<object> list = new List<object>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
var lst = list.Cast<string>().ToList();
то получим ошибку
OfType
Фильтрует значения в зависимости от возможности их приведения к указанному типу. Этот метод удобно использовать в том случае, если ваша коллекция содержит разнородные элементы и не все эти элементы могут быть приведены к определенному типу, например, с использованием метода Cast. Рассмотрим следующий пример:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return $"{Name} {Age} лет";
}
}
public class Employee : Person
{
public string Department { get; set; }
public string Role { get; set; }
public override string ToString()
{
return $"{base.ToString()} Отдел {Department} Должность {Role}";
}
}
internal class Program
{
static void Main(string[] args)
{
List<object> data = new List<object>()
{
"Строка",
12,
true,
new Person() { Name = "Вася", Age = 25 },
new Employee() { Name = "Коля", Department = "Курьерский отдел", Role = "Доставщик пиццы", Age = 25 }
};
var strList = data.Cast<string>().AsEnumerable();
var enumerator = strList.GetEnumerator();
while (enumerator.MoveNext())//<-- Ошибка
{
Console.WriteLine(enumerator.Current);
}
}
Здесь в список добавляются разнородные элементы — числа, строки и два объекта — базового класса Person и производного — Employee. При попытке выполнить метод Cast к списку мы получим ошибку, как было показано выше. Метод OfType позволяет выделить из исходной последовательности только те элементы, которые можно привести к заданному типу. То есть, при таком вызове:
var strList = data.OfType<string>().AsEnumerable();
var enumerator = strList.GetEnumerator();
while (enumerator.MoveNext())//<-- Ошибка
{
Console.WriteLine(enumerator.Current);
}
мы получим всего один элемент
Следующий вызов OfType вернет объекты Person и Employee:
List<object> data = new List<object>()
{
"Строка",
12,
true,
new Person() { Name = "Вася", Age = 25 },
new Employee() { Name = "Коля", Department = "Курьерский отдел", Role = "Доставщик пиццы", Age = 25 }
};
var strList = data.OfType<Person>().AsEnumerable();
var enumerator = strList.GetEnumerator();
while (enumerator.MoveNext())//<-- Ошибка
{
Console.WriteLine(enumerator.Current);
}
Результат:
Коля 25 лет Отдел Курьерский отдел Должность Доставщик пиццы
ToArray
Преобразует коллекцию в массив. Например:
var strList = data.OfType<Person>().ToArray();
foreach (Person person in strList)
{
Console.WriteLine(person);
}
здесь переменная strList будет иметь тип Person[].
ToList
Преобразует коллекцию в List<T>. Метод действует аналогично методу ToArray, но возвращает не массив, а универсальный список List<T>.
ToDictionary
Помещает элементы в Dictionary<TKey,TValue> в зависимости от функции выбора ключа. Например, в следующем примере коллекция List преобразуется в словарь с использованием в качестве ключа поля Name:
List<Employee> data = new List<Employee>()
{
new Employee() { Name = "Василий", Department = "Управление", Role = "Менеджер", Age = 25 },
new Employee() { Name = "Коля", Department = "Курьерский отдел", Role = "Почтальон", Age = 25 },
new Employee() { Name = "Петя", Department = "Курьерский отдел", Role = "Доставщик пиццы", Age = 25 }
};
var dict = data.ToDictionary(k => k.Name);
foreach (var element in dict)
{
Console.WriteLine($"{element.Key} - {element.Value}");
}
При выполнении метода ToDictionary следует учитывать, что указываемый ключ для словаря должен быть уникальный. Например, следующий код вызовет ошибку на этапе выполнения приложения:
var dict = data.ToDictionary(k => k.Department);
Этот метод удобно использовать, например, при запросе данных из какой-либо базы данных- в этом случае, можно получить словарь по уникальному ключу из таблицы БД. Если нам необходимо сгруппировать элементы по типу «один ко многим», то можно использовать следующий метод.
ToLookup
Помещает элементы в Lookup<TKey,TElement> (словарь «один ко многим») в зависимости от функции выбора ключа. Сгруппируем наш список работников по отделу в котором они работают:
List<Employee> data = new List<Employee>()
{
new Employee() { Name = "Василий", Department = "Управление", Role = "Менеджер", Age = 25 },
new Employee() { Name = "Коля", Department = "Курьерский отдел", Role = "Почтальон", Age = 25 },
new Employee() { Name = "Петя", Department = "Курьерский отдел", Role = "Доставщик пиццы", Age = 25 }
};
var dict = data.ToLookup(k => k.Department);
foreach (var element in dict)
{
Console.WriteLine($"{element.Key}");
foreach (var empl in element.ToArray())
Console.WriteLine($"{empl.ToString()}");
}
Здесь мы получили в переменной dict коллекцию типа ILookup<string, IEnumerable<Employee>> поэтому, дополнительно во вложенном цикле я вызвал метод ToArray, чтобы не выполнять дополнительные действия по получению Enumerator из коллекции работников. Результат работы приложения:
Василий 25 лет Отдел Управление Должность Менеджер
Курьерский отдел
Коля 25 лет Отдел Курьерский отдел Должность Почтальон
Петя 25 лет Отдел Курьерский отдел Должность Доставщик пиццы
Итого
Методы преобразования типов данных в LINQ позволяют, при необходимости, скрывать конкретную реализацию коллекции или преобразовать коллекцию одного типа, например, List<T> в коллекцию другого типа, например, Array<T>.