Upload
igor-shkulipa
View
57
Download
4
Embed Size (px)
Citation preview
Темы лекции: Лямбда-выражения. Обобщения.
Практическое задание: Лямбда-выражения. Обобщения.
Тренер: Игорь Шкулипа, к.т.н.
Платформа .Net и язык программирования C#.
Занятие 7
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);
}
Оператор => имеет тот же приоритет, что и оператор присваивания (=) иявляется правоассоциативным.
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
http://www.slideshare.net/IgorShkulipa 4
Тип Func<>
public delegate TResult Func<in T, out TResult>(T arg)
+ 17 перегрузок
T - тип параметра метода, инкапсулируемого данным делегатом.
Этот параметр типа является контрвариантным. Это означает, чтоможно использовать либо указанный тип, либо менее производныйтип.
TResult - тип возвращаемого значения метода, инкапсулируемого даннымделегатом.
Этот параметр типа является ковариантным. Это означает, что можноиспользовать либо указанный тип, либо более производный тип.
arg - параметр метода, инкапсулируемого данным делегатом.
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();
}
}
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();
}
}
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();
}
}
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();
}
}
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();
}
}
http://www.slideshare.net/IgorShkulipa 10
Factory Method
Паттерн Factory Method может быть полезным в решенииследующих задач:
1. Система должна оставаться расширяемой путем добавленияобъектов новых типов. Непосредственное использованиеоператора new является нежелательным, так как в этомслучае код создания объектов с указанием конкретных типовможет получиться разбросанным по всему приложению. Тогдатакие операции как добавление в систему объектов новыхтипов или замена объектов одного типа на другой будутзатруднительными. Паттерн Factory Method позволяет системеоставаться независимой как от самого процесса порожденияобъектов, так и от их типов.
1. Заранее известно, когда нужно создавать объект, нонеизвестен его тип.
http://www.slideshare.net/IgorShkulipa 11
Описание паттерна Factory Method
Для того, чтобы система оставалась независимой от различных типовобъектов, паттерн Factory Method использует механизм полиморфизма -классы всех конечных типов наследуются от одного абстрактногобазового класса, предназначенного для полиморфного использования. Вэтом базовом классе определяется единый интерфейс, через которыйпользователь будет оперировать объектами конечных типов.
Для обеспечения относительно простого добавления в систему новых типовпаттерн Factory Method локализует создание объектов конкретных типов вспециальном классе-фабрике. Методы этого класса, посредством которыхсоздаются объекты конкретных классов, называются фабричными.
Существуют две разновидности паттерна Factory Method:
Обобщенный конструктор, когда в том же самом полиморфном базовомклассе, от которого наследуются производные классы всех создаваемых всистеме типов, определяется статический фабричный метод. В качествепараметра в этот метод должен передаваться идентификатор типасоздаваемого объекта.
Классический вариант фабричного метода, когда интерфейс фабричныхметодов объявляется в независимом классе-фабрике, а их реализацияопределяется конкретными подклассами этого класса.
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";
}
}
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();
}
}
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
http://www.slideshare.net/IgorShkulipa 15
Преимущества и недостатки
Преимущества паттерна Factory Method:
◦ Создает объекты разных типов, позволяя системе оставатьсянезависимой как от самого процесса создания, так и оттипов создаваемых объектов.
Недостатки паттерна Factory Method:
◦ В случае классического варианта паттерна даже дляпорождения единственного объекта необходимо создаватьсоответствующую фабрику
http://www.slideshare.net/IgorShkulipa 16
Abstract Factory
Паттерн Abstract Factory стоит использовать, когда:
◦ Система должна оставаться независимой как от процессасоздания новых объектов, так и от типов порождаемыхобъектов. Непосредственное использование оператора new вкоде приложения нежелательно.
◦ Необходимо создавать группы или семействавзаимосвязанных объектов, исключая возможностьодновременного использования объектов из разныхсемейств в одном контексте.
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";
}
}
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";
}
}
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();
}
}
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
http://www.slideshare.net/IgorShkulipa 21
Обобщения
Обобщения (generics, универсальные шаблоны, универсальные типы)позволяют создавать открытые типы, которые преобразуются в закрытыево время выполнения.
public SomeCollection<T>
{
public SomeCollection() {}
private T[] data;
}
При объявлении такого типа, компилятор трактует тип Т, какнеуточненный.
При создании экземпляра этого класса, неуточненный тип будет замененконкретным типом.
Обобщения похожи на шаблоны С++ с тем отличием, что, если шаблоныС++ разворачиваются во время компиляции, то типы обобщенийуточняются во время выполнения.
http://www.slideshare.net/IgorShkulipa 22
Общие сведения об универсальных шаблонах
• Универсальные типы используются для достижения максимального уровняповторного использования кода, безопасности типа ипроизводительности.
• Наиболее частым случаем использования универсальных шаблоновявляется создание классов коллекции.
• Библиотека классов платформы .NET Framework содержит нескольконовых универсальных классов коллекций в пространстве именSystem.Collections.Generic. Их следует использовать по мере возможностивместо таких классов как ArrayList в пространстве именSystem.Collections.
• Можно создавать собственные универсальные интерфейсы, классы,методы, события и делегаты.
• Доступ универсальных классов к методам можно ограничитьопределенными типами данных.
• Сведения о типах, используемых в универсальном типе данных, можнополучить во время выполнения путем рефлексии.
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; }
}
Кроме обобщенных классов, есть так же возможностьсоздавать обобщенные интерфейсы и структуры, а так жеобобщенные делегаты.
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
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; }
}
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
http://www.slideshare.net/IgorShkulipa 27
Значение по умолчанию
По умолчанию экземпляры типов значений инициализируютсяустановкой всех битов в 0, а ссылочные типы – null.
При работе с обобщениями, компилятор заранее не знает, какой типбудет использован в каждом конкретном случае использования.
Для обобщений предусмотрен оператор default, который работает вовремя выполнения и инициализирует переменную указанного типазначением по умолчанию (0 для значений, null для ссылок)
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; }
}
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=
http://www.slideshare.net/IgorShkulipa 30
Обобщения и наследование
Обобщенные типы не могут наследоваться напрямую от параметровтипа, но могут наследоваться от других типов, в том числе иобобщенных.
//Так неправильно
public class MyClass<T>: T
{
}
//А так правильно
public class MyClass<T>: Stack<T>
{
}
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>
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
http://www.slideshare.net/IgorShkulipa 33
Лабораторная работа №7. Обобщения и лямбда-выражения
1. Спроектируйте и создайте обобщенный класс-коллекцию для созданияи обработки бинарного дерева. Создайте итераторы для прямого иобратного обхода дерева (операторы ++, --, методы Next(), Previous(),Current()).
Реализуйте интерфейс IEnumerable<T> так, чтобы созданное деревоможно было использовать в цикле foreach.
2. На основе лямбда-операторов, реализовать внешний итератор дляцентрального обхода дерева (делегат, реализованный на основелямбда-оператора возвращает, например, коллекцию вершин дерева,отсортированных в необходимой последовательности).