net framework и С#, весна 2015: linq
TRANSCRIPT
План лекции
• Введение• Средства реализации • Структура и методы LINQ• Ленивые вычисления
02.05.2023 Толстиков Никита 2LINQ
Пример• Дано :
• Найти:– Из 10 самых здоровых монстров с четными Id cтроку максимальной длинны состоящую из имени монстра и его здоровья
02.05.2023 Толстиков Никита 3LINQ
var monsters = new MonsterGenerator().Take(50).ToList();
Итеративный подход
02.05.2023 Толстиков Никита 4LINQ
static string IterativeMaxNameLength(List<Monster> monsters) { var filteredMonster = new List<Monster>(); foreach (var monster in monsters) { if (monster.Id % 2 == 0) { filteredMonster.Add(monster); } } filteredMonster.Sort(new MonstersHealthComparer()); int max = int.MinValue; string resultString = ""; for (int i = Math.Max(0, filteredMonster.Count - 10); i < filteredMonster.Count; i++) { string currentString =
String.Format("{0} {1}", filteredMonster[i].Name, filteredMonster[i].Health); var currentCoef = currentString.Length; if (currentCoef > max) { max = currentCoef; resultString = currentString; } } return resultString; }
LINQ
02.05.2023 Толстиков Никита 5LINQ
private static string LinqMaxNameLength(IEnumerable<Monster> monsters) { return monsters.Where(monster => monster.Id % 2 == 0) .OrderByDescending(monster => monster.Health) .Take(10) .Select(monster => String.Format("{0} {1}", monster.Name, monster.Health)) .OrderByDescending(s => s.Length) .First(); }
Почему работает?• Неявно типизированные
локальные переменные• Синтаксис инициализации
объектов• Lambda-функции• Extension-методы• Анонимные типы
02.05.2023 Толстиков Никита 6LINQ
Почему работает?• Синтаксис инициализации
объектов• Extension-методы• Неявно типизированные
локальные переменные• Анонимные типы• Lambda-функции
02.05.2023 Толстиков Никита 7LINQ
Неявная типизация
– Компилятор сам выводит тип и подставляет его во время компиляции
– Позволяет сократить кол-во написанного кода
– Неявно типизировать можно только локальные переменные или типовые параметры
02.05.2023 Толстиков Никита 8LINQ
var dict = new Dictionary<string, List<Tuple<Monster, string, int>>>();
Анонимные типы
– Компилятор может вывести поля типа только внутри метода
– Не стоит передавать в методы– Нельзя реализовывать интерфейсы– Нельзя создавать методы–Можно использовать как ключи в
словаре02.05.2023 Толстиков Никита 9LINQ
var nameFamily = new {Name = "Иван", Family = "Иванов"};Console.WriteLine(nameFamily.GetType()); //<>f__AnonymousType0`2[System.String, System.String]Console.WriteLine("{0} {1}", nameFamily.Name, nameFamily.Family);
Lambda-функции• Функции которыми можно
манипулировать как переменными• Для введени используется символ
‘=>’• Синтаксис:
(<arguments>) => { <code> }
– все скобки могут быть опущены02.05.2023 Толстиков Никита 10LINQ
Lambda-функции• Функции которыми можно
манипулировать как переменными• Для введени используется символ
‘=>’• Синтаксис:
(<arguments>) => { <code> }
– все скобки могут быть опущены02.05.2023 Толстиков Никита 11LINQ
Lambda-функции• Пример:
– оператор ‘=>’ может читаться как «goes to»
02.05.2023 Толстиков Никита 12LINQ
List<int> elements = new List<int>() { 10, 20, 31, 40 };// ... Find index of first odd element.int oddIndex = elements.FindIndex(x => x % 2 != 0);Console.WriteLine(oddIndex);
Lambda-функции
Какой тип у lambda-функции?
02.05.2023 Толстиков Никита 13LINQ
Делегат• Тип описывающий метод с определенной
сигнатурой:
• Инициализация объекта типа:– через метод
– через lambda-фунцию
02.05.2023 Толстиков Никита 14LINQ
public delegate void WriteLog(string message);
public static void FileWriteLog(string message){
//Write log to file}...WriteLog method = FileWriteLog;
method = log => { Console.WriteLine("log"); };
Делегат• Исполнить делегат или выполнить
метод на который он указывает:
– лямбда функция запоминает контекст– у типа делегат переопределен оператор ()– аналогично можно вызвать метод Invoke()
02.05.2023 Толстиков Никита 15LINQ
WriteLog method = FileWriteLog;method.Invoke("FileLog"); //Вывести лог в файл
method = log => { Console.WriteLine("log"); };method.Invoke("ConsoleLog"); //Вывести лог на консоль
method("SomeLog"); //Вывести лог на консоль
Lambda-функции• Типы lambda-функций:– процедуры:
–функции:
02.05.2023 Толстиков Никита 16LINQ
public delegate void Action<in T, in T1 ...>(T obj, T1 obj1 ...);
public delegate TResult Action<in T, in T1 ..., out TResult>
(T obj, T1 obj1 ...);
Lambda-функции• Пример:
02.05.2023 Толстиков Никита 17LINQ
// Use implicitly typed lambda expression.// ... Assign it to a Func instance.Func<int, int> func1 = x => x + 1;// Use lambda expression with statement body.Func<int, int> func2 = x => { return x + 1; };// Use formal parameters with expression body.Func<int, int> func3 = (int x) => x + 1;// Use parameters with a statement body.Func<int, int> func4 = (int x) => { return x + 1; };// Use multiple parameters.Func<int, int, int> func5 = (x, y) => x * y;// Use no parameters in a lambda expression.Action func6 = () => Console.WriteLine();// Use delegate method expression.Func<int, int> func7 = delegate(int x) { return x + 1; };// Use delegate expression with no parameter list.Func<int> func8 = delegate { return 1 + 1; };
Почему работает?
02.05.2023 Толстиков Никита 18LINQ
LINQ
02.05.2023 Толстиков Никита 19LINQ
Группы операций• Фильтры (Where)• Проекции (Select, SelectMany)• Сортировка (OrderBy)• Групировки (GroupBy)• Оперции над множествами (Distinct, Union, Intersect, Except)
• Преобразования (ToList, ToDictionary)• Элементы (First, FirstOrDefault, Last)• Кванторы (All, Any)• Агрегаторы (Count,Sum,Max/Min, Aggregate)
02.05.2023 Толстиков Никита 20LINQ
Фильтрыstring[] digits = { "zero", "one", "two",
"three", "four", "five", "six", "seven", "eight", "nine" };
// Фильтр foreachvar oddsDigits = digits.Where(digit => digit.Length % 2 == 0);
// Фильтр foreach c индексомvar shortDigits = digits.Where((digit, index) => digit.Length <
index);
02.05.2023 Толстиков Никита 21LINQ
Проекции
02.05.2023 Толстиков Никита 22LINQ
• Позволяют получать из одних объектов другие
• SelectMany схлопывает последовательности в одну://return IEnumerable<char>
var digitChars = digits.SelectMany( stringDigit => stringDigit.ToCharArray());
//return IEnumerable<int>var intDigits = digits.Select( stringDigit => ConvertStringDigitToInt(stringDigit));
Сортировки
02.05.2023 Толстиков Никита 23LINQ
• OrderBy упорядочивает последовательность и возвращает IOrderedEnumerable
• Для сортировки по нескольким значениям лучше использовать ThenBy:monsters.OrderBy(monster => monster.Id)
.ThenBy(monsters => monsters.Health);
var monsters = new MonsterGenerator().Take(50);
monsters.OrderBy(monster => monster.Id);monsters.OrderByDescending(monster => monster.Health);
Группировки
02.05.2023 Толстиков Никита 24LINQ
• GroupBy групирует элементы по ключу и возвращает:
IEnumerable<IGrouping<TKey, TSource>>
– первый параметр – это функция определяющая ключ
– перегрузки позволяют манипулировать группами (проецировать, фильтровать, выбирать свой компаратор и т.д.)
Группировки
02.05.2023 Толстиков Никита 25LINQ
var groups = monsters.GroupBy(monster => { if (monster.Health < 10) { return 0; }else if (10 <= monster.Health && monster.Health < 70) { return 10; } else if (70 <= monster.Health && monster.Health < 100) { return 70; } else { return 100; } });
foreach (var group in groups) { Console.WriteLine("{0} : {1}", group.Key, group.Count()); }
Трансформаторы
02.05.2023 Толстиков Никита 26LINQ
• ToList – трансформирует перечисление в List:
• ToDicitionary трансформирует Ienumerable в словарь:
• OfType элементы IEnumerable к типу параметра:
List<Monster> monsters = new MonsterGenerator().Take(50).ToList();
Dictionary<string, Monster> monstersDicitionary = monsters.ToDictionary(monster =>
monster.Name);
object[] numbers = { null, 1.0, "two", 3, "four", 5, "six", 7.0 };
var doubles = numbers.OfType<double>();
Элементы
02.05.2023 Толстиков Никита 27LINQ
• First/Last - возвращает первый/последний элемент из последовательности удовлетворяющий условию:
– выбрасывает InvalidOperationException, если таких элементов нет
• FirstOrDefault/LastOrDefault – аналогичны, но если элемент не найден – то возвращается default(T)
monsters.First(monster => monster.Name.Length > 10);
Кванторы
02.05.2023 Толстиков Никита 28LINQ
• Проверяют, что все (All) или хотя бы один (Any) элемент из последовательности удовлетворяет условию:
monsters.Where(monster => monster.Name.Length > 6) .Any(monster => monster.Health > 75);
Агрегаторы
02.05.2023 Толстиков Никита 29LINQ
• Сворачивают все элементы последовательности в один, находя максимум, минимум, количество элементов или сумму последовательности:
• Aggregate – применяет функцию агрегатор на последовательности:
monsters.Where(monster => monster.Name.Length > 6) .Count(monster => monster.Health > 75);
monsters.Where(monster => monster.Name.Length > 6) .Max(monster => monster.Health);
//вернет все имена монстров в одной строке разделенные пробеломmonsters.Aggregate("Monsters: ", (seed, monster) => seed + " " + monster.Name);
Подитог
02.05.2023 Толстиков Никита 30LINQ
• LINQ – привносит функциональный стиль в ООП
• Позволяет писать запросы к различным источникам с удобным синтаксисом
• Имеет множество функций и их перегрузок
• Поддерживает ленивые вычисления
Ленивые вычисления• Вычисления откладываются до тех
пор пока не понадобится их результаты
• Достаточно описать зависимости функций друг от друга и не следить за тем, чтобы не осуществлялось «лишних вычислений»
02.05.2023 Толстиков Никита 31LINQ
Пример
02.05.2023 Толстиков Никита 32LINQ
monsters = new List<Monster> { new Monster(1), new Monster(50), new Monster(51) };
var monsterIdMoreThan50 = monsters.Where(monster => monster.Id >= 50);
monsters.Add(new Monster(100));
Console.WriteLine(monsterIdMoreThan50.Count()); //Output: 3
yield return• Ставит метод на «паузу»
возвращая значение• При следующем вызове, метод
продолжит исполнение с последнего yield return
02.05.2023 Толстиков Никита 33LINQ
yield return
02.05.2023 Толстиков Никита 34LINQ
public class YieldingClass { public IEnumerable<int> GetFibonachiSequence() { yield return 1;
yield return 1; yield return 2; yield return 3; yield return 5; } }
yield return
02.05.2023 Толстиков Никита 35LINQ
//Output: 1 1 2foreach (var number in fibonachi){
Console.WriteLine(number);count++;if (count == 3) break;
}
//Output: 3 5foreach (var number in fibonachi){
Console.WriteLine(number);}
yield return
02.05.2023 Толстиков Никита 36LINQ
[CompilerGenerated]private sealed class YieldingEnumerator : IEnumerable<object>, IEnumerator<object>{
// Fieldsprivate int state;private int current;public YieldingClass owner;private int initialThreadId;
// Methods[DebuggerHidden]public YieldingEnumerator(int state);private bool MoveNext();[DebuggerHidden]IEnumerator<int> IEnumerable<int>.GetEnumerator();[DebuggerHidden]IEnumerator IEnumerable.GetEnumerator();[DebuggerHidden]void IEnumerator.Reset();void IDisposable.Dispose();
// Propertiesobject IEnumerator<object>.Current{ [DebuggerHidden] get; }
object IEnumerator.Current{ [DebuggerHidden] get; }
}
Состояния
02.05.2023 Толстиков Никита 37LINQ
Метод GetFibonachiSequence() просто возвращает YieldingEnumerator, и устанавливает его state в состояние -2
Какждый enumerator поддерживают состояния, как число:
-2: Инициализирован как Enumerable-1: Закрыт0: Инийиализирован как Enumerator1-n: Индекс yield return оператора в GetFibonachiSequence() методе
Если от объекта YieldingClass, вызвать повторно GetFibonachiSequence(), то вернется новый YieldingEnumerator
Итог
02.05.2023 Толстиков Никита 38LINQ
• С# предлагает набор инструментов для реализации ленивого вычисления
• Ленивые вычисления дают возможность обрабатывать данные порциями
• LINQ – отличный модуль для написания запросов и составления pipline’ов
The End
02.05.2023 Толстиков Никита 39LINQ