33
Темы лекции: Лямбда-выражения. Обобщения. Практическое задание: Лямбда-выражения. Обобщения. Тренер: Игорь Шкулипа, к.т.н. Платформа .Net и язык программирования C#. Занятие 7

C# Desktop. Занятие 07

Embed Size (px)

Citation preview

Page 1: C# Desktop. Занятие 07

Темы лекции: Лямбда-выражения. Обобщения.

Практическое задание: Лямбда-выражения. Обобщения.

Тренер: Игорь Шкулипа, к.т.н.

Платформа .Net и язык программирования C#.

Занятие 7

Page 2: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 2

Лямбда-выражения

Лямбда-выражение — это анонимная функция, которую можноиспользовать для создания таких типов как делегаты или деревьявыражений. С помощью лямбда-выражений можно написать локальныефункции, которые затем можно передавать в другие функции вкачестве аргументов или возвращать из них в качестве значения.

Для создания лямбда-выражения можно определить входные параметры(если таковые имеются) слева лямбда-оператора => и поместить блоквыражений или выписки на другую сторону. Например, лямбда-выражение x => x * x принимает параметр с именем x и возвращаетзначение x, возведённое в квадрат.

delegate int Mult(int i);

static void Main(string[] args)

{

Mult multDelegate = x => x * x;

int j = multDelegate(5);

}

Оператор => имеет тот же приоритет, что и оператор присваивания (=) иявляется правоассоциативным.

Page 3: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 3

Типы аргументов

public class LambdaExpressions

{

static void Main(string[] args)

{

Func<int, double> expr = x => x / 2;

int i = 5;

Console.WriteLine(expr(i));

Console.ReadKey();

}

}

2

public class LambdaExpressions

{

static void Main(string[] args)

{

Func<double, double> expr = (double x) => x / 2;

int i = 5;

Console.WriteLine(expr(i));

Console.ReadKey();

}

}

2,5

Page 4: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 4

Тип Func<>

public delegate TResult Func<in T, out TResult>(T arg)

+ 17 перегрузок

T - тип параметра метода, инкапсулируемого данным делегатом.

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

TResult - тип возвращаемого значения метода, инкапсулируемого даннымделегатом.

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

arg - параметр метода, инкапсулируемого данным делегатом.

Page 5: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 5

Лямбда-операторы

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

По причине того, что операторов, в общем случае, несколько такие лямбда-операторы должны содержать оператор return;

(x, y)=> x * y;

(x, y)=> { return x*y; }

Пример:

public delegate double SomeLambda(double x, double y);

public class LambdaExpressions

{

static void Main(string[] args)

{

SomeLambda lamdaDelegate = (x, y) =>

{

double xx = x * x;

double yy = y * y;

return Math.Sqrt(xx + yy);

};

Console.WriteLine(lamdaDelegate(3,4));

Console.ReadKey();

}

}

Page 6: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 6

Деревья выражений

Используя тип Expression<T> из пространства именSystem.Linq.Expressions, можно составлять из лямбда-выраженийдеревья выражений.

public class LambdaExpressions

{

static void Main(string[] args)

{

Expression<Func<double, double>> expr = n => n + 1;

Func<double, double> func = expr.Compile();

for (int i = 0; i < 10; i++)

{

Console.WriteLine(func(i));

}

Console.ReadKey();

}

}

Page 7: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 7

Деревья выражений

public class LambdaExpressions

{

static void Main(string[] args)

{

ParameterExpression number =

Expression.Parameter(typeof(double), "num");

ConstantExpression one =

Expression.Constant((double)1, typeof(double));

BinaryExpression add =

Expression.Add(number, one);

Expression<Func<double, double>> lambda1 =

Expression.Lambda<Func<double, double>>(

add, new ParameterExpression[] { number });

Func<double, double> func = lambda1.Compile();

for (int i = 0; i < 10; i++)

{

Console.WriteLine(func(i));

}

Console.ReadKey();

}

}

Page 8: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 8

Блоки выражений

public class LambdaExpressions

{

static void Main(string[] args)

{

ParameterExpression value = Expression.Parameter(typeof(int), "value");

ParameterExpression result = Expression.Parameter(typeof(int), "result");

LabelTarget label = Expression.Label(typeof(int));

BlockExpression block = Expression.Block(

new[] { result },

Expression.Assign(result, Expression.Constant(1)),

Expression.Loop(

Expression.IfThenElse(

Expression.GreaterThan(value, Expression.Constant(1)),

Expression.MultiplyAssign(result,

Expression.PostDecrementAssign(value)),

Expression.Break(label, result)

),

label

)

);

int factorial =

Expression.Lambda<Func<int, int>>(block, value).Compile()(5);

Console.WriteLine(factorial);

Console.ReadKey();

}

}

Page 9: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 9

Анонимная рекурсия

public class LambdaExpressions

{

static void Main(string[] args)

{

Func<int, int> fact = null;

fact = (x) => x > 1 ? x * fact(x - 1) : 1;

Console.WriteLine(fact(5));

Console.ReadKey();

}

}

Page 10: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 10

Factory Method

Паттерн Factory Method может быть полезным в решенииследующих задач:

1. Система должна оставаться расширяемой путем добавленияобъектов новых типов. Непосредственное использованиеоператора new является нежелательным, так как в этомслучае код создания объектов с указанием конкретных типовможет получиться разбросанным по всему приложению. Тогдатакие операции как добавление в систему объектов новыхтипов или замена объектов одного типа на другой будутзатруднительными. Паттерн Factory Method позволяет системеоставаться независимой как от самого процесса порожденияобъектов, так и от их типов.

1. Заранее известно, когда нужно создавать объект, нонеизвестен его тип.

Page 11: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 11

Описание паттерна Factory Method

Для того, чтобы система оставалась независимой от различных типовобъектов, паттерн Factory Method использует механизм полиморфизма -классы всех конечных типов наследуются от одного абстрактногобазового класса, предназначенного для полиморфного использования. Вэтом базовом классе определяется единый интерфейс, через которыйпользователь будет оперировать объектами конечных типов.

Для обеспечения относительно простого добавления в систему новых типовпаттерн Factory Method локализует создание объектов конкретных типов вспециальном классе-фабрике. Методы этого класса, посредством которыхсоздаются объекты конкретных классов, называются фабричными.

Существуют две разновидности паттерна Factory Method:

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

Классический вариант фабричного метода, когда интерфейс фабричныхметодов объявляется в независимом классе-фабрике, а их реализацияопределяется конкретными подклассами этого класса.

Page 12: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 12

Классическая реализация Factory Method. Классы-результаты фабрики

public abstract class BaseClass

{

public abstract string GetName();

}

public class DerivedClass1 : BaseClass

{

public override string GetName()

{

return "Derived Class 1";

}

}

public class DerivedClass2 : BaseClass

{

public override string GetName()

{

return "Derived Class 2";

}

}

Page 13: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 13

Классическая реализация Factory Method. Классы фабрик

public abstract class Factory

{

public abstract BaseClass FactoryMethod();

}

class Factory1 : Factory

{

public override BaseClass FactoryMethod()

{

return new DerivedClass1();

}

}

class Factory2 : Factory

{

public override BaseClass FactoryMethod()

{

return new DerivedClass2();

}

}

Page 14: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 14

Классическая реализация Factory Method. Использование

class Program

{

static void Main(string[] args)

{

Factory1 fact1 = new Factory1();

Factory2 fact2 = new Factory2();

Factory[] factories = { fact1, fact2 };

for (int i = 0; i < factories.Length; i++)

{

BaseClass bc = factories[i].FactoryMethod();

Console.WriteLine(

"Type={0}, Object={1}",

bc.GetType().ToString(), bc.GetName());

}

Console.ReadKey();

}

}

Type=HelloWorld.DerivedClass1, Object=Derived Class 1

Type=HelloWorld.DerivedClass2, Object=Derived Class 2

Page 15: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 15

Преимущества и недостатки

Преимущества паттерна Factory Method:

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

Недостатки паттерна Factory Method:

◦ В случае классического варианта паттерна даже дляпорождения единственного объекта необходимо создаватьсоответствующую фабрику

Page 16: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 16

Abstract Factory

Паттерн Abstract Factory стоит использовать, когда:

◦ Система должна оставаться независимой как от процессасоздания новых объектов, так и от типов порождаемыхобъектов. Непосредственное использование оператора new вкоде приложения нежелательно.

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

Page 17: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 17

Классы для 1-й фабрики

public abstract class BaseClass1

{

public abstract string GetName();

}

public class DerivedClass11 : BaseClass1

{

public override string GetName()

{

return "Derived Class 11";

}

}

public class DerivedClass21 : BaseClass1

{

public override string GetName()

{

return "Derived Class 21";

}

}

Page 18: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 18

Классы для 2-й фабрики

public abstract class BaseClass2

{

public abstract string GetName();

}

public class DerivedClass12 : BaseClass2

{

public override string GetName()

{

return "Derived Class 12";

}

}

public class DerivedClass22 : BaseClass2

{

public override string GetName()

{

return "Derived Class 22";

}

}

Page 19: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 19

Классы фабрик

public abstract class AbstractFactory

{

public abstract BaseClass1 FactoryMethod1();

public abstract BaseClass2 FactoryMethod2();

}

class AbstractFactory1 : AbstractFactory {

public override BaseClass1 FactoryMethod1() {

return new DerivedClass11();

}

public override BaseClass2 FactoryMethod2() {

return new DerivedClass12();

}

}

class AbstractFactory2 : AbstractFactory {

public override BaseClass1 FactoryMethod1() {

return new DerivedClass21();

}

public override BaseClass2 FactoryMethod2() {

return new DerivedClass22();

}

}

Page 20: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 20

Использованиеclass Program

{

static void UseAbstractFactory(AbstractFactory af)

{

BaseClass1 bc1 = af.FactoryMethod1();

BaseClass2 bc2 = af.FactoryMethod2();

Console.WriteLine(bc1.GetName());

Console.WriteLine(bc2.GetName());

}

static void Main(string[] args)

{

AbstractFactory af1 = new AbstractFactory1();

AbstractFactory af2 = new AbstractFactory2();

UseAbstractFactory(af1);

UseAbstractFactory(af2);

Console.ReadKey();

}

}

Derived Class 11

Derived Class 12

Derived Class 21

Derived Class 22

Page 21: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 21

Обобщения

Обобщения (generics, универсальные шаблоны, универсальные типы)позволяют создавать открытые типы, которые преобразуются в закрытыево время выполнения.

public SomeCollection<T>

{

public SomeCollection() {}

private T[] data;

}

При объявлении такого типа, компилятор трактует тип Т, какнеуточненный.

При создании экземпляра этого класса, неуточненный тип будет замененконкретным типом.

Обобщения похожи на шаблоны С++ с тем отличием, что, если шаблоныС++ разворачиваются во время компиляции, то типы обобщенийуточняются во время выполнения.

Page 22: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 22

Общие сведения об универсальных шаблонах

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

• Наиболее частым случаем использования универсальных шаблоновявляется создание классов коллекции.

• Библиотека классов платформы .NET Framework содержит нескольконовых универсальных классов коллекций в пространстве именSystem.Collections.Generic. Их следует использовать по мере возможностивместо таких классов как ArrayList в пространстве именSystem.Collections.

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

• Доступ универсальных классов к методам можно ограничитьопределенными типами данных.

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

Page 23: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 23

Обобщенные классы

class GenericClass<Type1, Type2>

{

public GenericClass(Type1 x, Type2 y) {

Field1 = x;

Field2 = y;

}

public void Print()

{

Console.WriteLine(

"Type1={0}, Field1={1}\nType2={2}, Field2={3}",

typeof(Type1).ToString(), Field1.ToString(),

typeof(Type2).ToString(), Field2.ToString());

}

public Type1 Field1 { get; set; }

public Type2 Field2 { get; set; }

}

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

Page 24: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 24

Использование обобщенных типов

class Program

{

static void Main(string[] args)

{

GenericClass<int, double> gc1 =

new GenericClass<int, double>(1, 2);

GenericClass<double, double> gc2 =

new GenericClass<double, double>(2, 3);

GenericClass<Complex, double> gc3 =

new GenericClass<Complex, double>(new Complex(3, 2), 2);

gc1.Print();

gc2.Print();

gc3.Print();

Console.ReadKey();

}

} Type1=System.Int32, Field1=1

Type2=System.Double, Field2=2

Type1=System.Double, Field1=1

Type2=System.Double, Field2=2

Type1=HelloWorld.Complex, Field1=3+i2

Type2=System.Double, Field2=2

Page 25: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 25

Обобщенные методы

class GenericClass<Type1, Type2>

{

public GenericClass(Type1 x, Type2 y) {

Field1 = x;

Field2 = y;

}

public void ExtendedPrint<Type3>(Type3 z)

{

this.Print();

Console.WriteLine("Type3={0}, Parameter={1}",

typeof(Type3).ToString(), z.ToString());

}

public void Print()

{

Console.WriteLine(

"Type1={0}, Field1={1}\nType2={2}, Field2={3}",

typeof(Type1).ToString(), Field1.ToString(),

typeof(Type2).ToString(), Field2.ToString());

}

public Type1 Field1 { get; set; }

public Type2 Field2 { get; set; }

}

Page 26: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 26

Использование обобщенных методовpublic delegate Complex ComplAction(Complex c);

class Program

{

static void Main(string[] args)

{

GenericClass<int, double> gc1 =

new GenericClass<int, double>(1, 2);

GenericClass<double, double> gc2 =

new GenericClass<double, double>(1, 2);

GenericClass<Complex, double> gc3 =

new GenericClass<Complex, double>(new Complex(3, 2), 2);

Complex c1 = new Complex(5, 6);

ComplAction action = c1.Add;

gc1.ExtendedPrint<string>("Hello!");

gc2.ExtendedPrint<ComplAction>(action);

gc3.ExtendedPrint<Delegate>(action);

Console.ReadKey();

}

}Type1=System.Int32, Field1=1

Type2=System.Double, Field2=2

Type3=System.String, Parameter=Hello!

Type1=System.Double, Field1=1

Type2=System.Double, Field2=2

Type3=HelloWorld.ComplAction, Parameter=HelloWorld.ComplAction

Type1=HelloWorld.Complex, Field1=3+i2

Type2=System.Double, Field2=2

Type3=System.Delegate, Parameter=HelloWorld.ComplAction

Page 27: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 27

Значение по умолчанию

По умолчанию экземпляры типов значений инициализируютсяустановкой всех битов в 0, а ссылочные типы – null.

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

Для обобщений предусмотрен оператор default, который работает вовремя выполнения и инициализирует переменную указанного типазначением по умолчанию (0 для значений, null для ссылок)

Page 28: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 28

Пример

class GenericClass<Type1, Type2>

{

public GenericClass(Type1 x, Type2 y) {

Field1 = x; Field2 = y;

}

public void DefaultPrint<Type3>() {

Field1 = default(Type1);

Field2 = default(Type2);

Type3 zz = default(Type3);

ExtendedPrint<Type3>(zz);

}

public void ExtendedPrint<Type3>(Type3 z) {

this.Print();

Console.WriteLine("Type3={0}, Parameter={1}",

typeof(Type3).ToString(), Convert.ToString(z));

}

public void Print() {

Console.WriteLine("Type1={0}, Field1={1}\nType2={2}, Field2={3}",

typeof(Type1).ToString(), Convert.ToString(Field1),

typeof(Type2).ToString(), Convert.ToString(Field2));

}

public Type1 Field1 { get; set; }

public Type2 Field2 { get; set; }

}

Page 29: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 29

Пример

public delegate Complex ComplAction(Complex c);

class Program

{

static void Main(string[] args)

{

GenericClass<int, double> gc1 =

new GenericClass<int, double>(1, 2);

GenericClass<double, double> gc2 =

new GenericClass<double, double>(1, 2);

GenericClass<Complex, double> gc3 =

new GenericClass<Complex, double>(new Complex(3, 2), 2);

Complex c1 = new Complex(5, 6);

ComplAction action = c1.Add;

gc1.DefaultPrint<string>();

gc2.DefaultPrint<ComplAction>();

gc3.DefaultPrint<Delegate>();

Console.ReadKey();

}

}

Type1=System.Int32, Field1=0

Type2=System.Double, Field2=0

Type3=System.String, Parameter=

Type1=System.Double, Field1=0

Type2=System.Double, Field2=0

Type3=HelloWorld.ComplAction, Parameter=

Type1=HelloWorld.Complex, Field1=

Type2=System.Double, Field2=0

Type3=System.Delegate, Parameter=

Page 30: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 30

Обобщения и наследование

Обобщенные типы не могут наследоваться напрямую от параметровтипа, но могут наследоваться от других типов, в том числе иобобщенных.

//Так неправильно

public class MyClass<T>: T

{

}

//А так правильно

public class MyClass<T>: Stack<T>

{

}

Page 31: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 31

Интерфейсы обобщенных коллекций

В пространстве имен System.Collections.Generic определен целый рядинтерфейсов обобщенных коллекций, имеющих соответствующие аналоги средиинтерфейсов необобщенных коллекций:

ICollection<T> - Определяет основополагающие свойства обобщенных коллекций

IComparer<T> - Определяет обобщенный метод Compare() для сравненияобъектов, хранящихся в коллекции

IDictionary<Tkey, TValue> - Определяет обобщенную коллекцию, состоящую изпар "ключ-значение"

IEnumerable<T> - Определяет обобщенный метод GetEnumerator(),предоставляющий перечислитель для любого класса коллекции

Enumerator<T> - Предоставляет методы, позволяющие получать содержимоеколлекции по очереди

IEqualityComparer<T> - Сравнивает два объекта на предмет равенства

IList<T> - Определяет обобщенную коллекцию, доступ к которой можно получитьс помощью индексатора

Структура KeyValuePair<TKey, TValue> - Служит для хранения ключа и егозначения и применяется в классах обобщенных коллекций, в которых хранятсяпары "ключ-значение", как, например, в классе Dictionary<TKey, TValue>

Page 32: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 32

Классы обобщенных коллекций

Kлассы обобщенных коллекций по большей части соответствуют своим необобщенныманалогам, хотя в некоторых случаях они носят другие имена. Отличаются они такжесвоей организацией и функциональными возможностями. Классы обобщенныхколлекций определяются в пространстве имен System.Collections.Generic:

Dictionary<Tkey, TValue> - Сохраняет пары "ключ-значение". Обеспечивает такиеже функциональные возможности, как и необобщенный класс Hashtable

HashSet<T> - Сохраняет ряд уникальных значений, используя хештаблицу

LinkedList<T> - Сохраняет элементы в двунаправленном списке

List<T> - Создает динамический массив. Обеспечивает такие же функциональныевозможности, как и необобщенный класс ArrayList

Queue<T> - Создает очередь. Обеспечивает такие же функциональные возможности,как и необобщенный класс Queue

SortedDictionary<TKey, TValue> - Создает отсортированный список из пар "ключ-значение"

SortedList<TKey, TValue> - Создает отсортированный список из пар "ключ-значение". Обеспечивает такие же функциональные возможности, как инеобобщенный класс SortedList

SortedSet<T> - Создает отсортированное множество

Stack<T> - Создает стек. Обеспечивает такие же функциональные возможности, как инеобобщенный класс Stack

Page 33: C# Desktop. Занятие 07

http://www.slideshare.net/IgorShkulipa 33

Лабораторная работа №7. Обобщения и лямбда-выражения

1. Спроектируйте и создайте обобщенный класс-коллекцию для созданияи обработки бинарного дерева. Создайте итераторы для прямого иобратного обхода дерева (операторы ++, --, методы Next(), Previous(),Current()).

Реализуйте интерфейс IEnumerable<T> так, чтобы созданное деревоможно было использовать в цикле foreach.

2. На основе лямбда-операторов, реализовать внешний итератор дляцентрального обхода дерева (делегат, реализованный на основелямбда-оператора возвращает, например, коллекцию вершин дерева,отсортированных в необходимой последовательности).