Занятие №8 на курса по програмиране на C# 2013 провеждан от ДАВИД академия в ПМГ "Никола Обрешков" - Казанлък. Включва темите:
- Полиморфизъм
- Абстракция
- Шаблони
- Вградени колекции
3. Къде е грешката в кода?
class Animal
{
public string Species { get; private set; }
public Animal(string species)
{
Species = species;
}
}
class Lizard : Animal
{
public Lizard(string species)
{
Species = species;
}
}
4. Къде е грешката в кода?
class Person
{
private string _name;
public string Name
{
get { return _name; }
set { }
}
}
class Program
{
static void Main(string[] args)
{
Person person = new Person();
Console.WriteLine("Въведете име на човек: ");
person.Name = Console.ReadLine();
Console.ReadLine();
}
}
5. Какво ще се случи?
class Person
{
public string Name { get; set; }
public void InputFromConsole()
{
Console.WriteLine("Въведете име на човек: ");
Name = Console.ReadLine();
}
}
class Program
{
static void Main(string[] args)
{
try
{
Person[] persons = new Person[5];
for (int i = 0; i < persons.Length; i++)
persons[i].InputFromConsole();
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Възникна IndexOutOfRangeException.");
}
catch (IOException ex)
{
Console.WriteLine("Възникна IOException.");
}
Console.ReadLine();
}
}
6. Полиморфизъм
• Какво е „полиморфизъм“?
– Третият основен принцип на ООП
– Множественост на формите (многообразие)
– Един член има няколко различни форми или
поведения
– Сходната функционалност се обединява по подходящ
начин
– Гъвкав и лесен за поддръжка програмен код
7. Полиморфизъм
• Полиморфизъм в C#
–
–
–
–
–
Презаписване на членове
Предефиниране на членове
Абстрактни класове и членове
Интерфейси
Шаблони
8. Презаписване на членове
• Какво е „презаписване“ на членове
(overloading)?
– Деклариране на повече от един член с еднакви
наименования
– Различни по брой или тип параметри
– Методи, конструктори, индексатори, оператори
– Компилаторът открива най-подходящият член, който
да бъде изпълнен, според подадените аргументи
9. Презаписване на членове
class Dog
{
public Dog(string name)
: this(name, 4)
{
}
public Dog(string name, int legsCount)
{
Name = name;
LegsCount = legsCount;
}
public string Name { get; set; }
public int LegsCount { get; set; }
public void BarkAt(string someone)
{
Console.WriteLine("{0} barks at {1}.", Name, someone);
}
public void BarkAt(Dog otherDog)
{
Console.WriteLine("{0} barks at {1}.", Name, otherDog.Name);
}
}
10. Презаписване на членове
class Dogs
{
private readonly Dog[] _dogs = new Dog[50];
public Dog this[int index]
{
get { return _dogs[index]; }
set { _dogs[index] = value; }
}
public Dog this[string name]
{
get
{
foreach (Dog dog in _dogs)
{
if (dog != null && dog.Name == name)
return dog;
}
return null;
}
}
}
11. Презаписване на членове
• Използване на презаписани членове
– Компилаторът търси член с подходящи параметри
– Броят на параметрите трябва да съвпада с броя на
аргументите
– Предимство се дава на член с параметри, чиито
типове съвпадат с тези на аргументите
– Неявно превръщане на типовете на аргументите
(upcasting и implicit typecasting)
– Ако има няколко възможни члена с еднакъв
приоритет, компилаторът предизвиква грешка
12. Презаписване на членове
• Приложение на презаписването на членове
– Прилагане на сходна функционалност върху различни
типове данни
– Прави кода по-четим, тъй като сходните операции
имат еднакви имена
– Да не се злоупотребява: прилага се, когато
поведението е еднотипно и типът на резултата е един
и същи
13. Предефиниране на членове
• Какво е „предефиниране“ на членове
(overriding)?
– Променяне на поведението на член в наследен клас
– Всеки клас от йерархията може да опише различни
операции за членове с еднакви наименования и
параметри
– Операциите, които ще бъдат изпълнени, зависят от
истинския тип на обекта, не от типа на рефернцията
към него
– Само виртуални членове могат да бъдат
предефинирани
14. Предефиниране на членове
• Какво е „виртуален член“?
– Член, който може да бъде предефиниран в наследен
клас
– Нестатични методи, свойства, индексатори
– Членовете по подразбиране са невиртуални
15. Предефиниране на членове
• При невиртуалните членове операциите, които
ще бъдат изпълнени, се определят от типа на
референцията, чийто член се изпълнява
• При виртуалните членове операциите, които ще
бъдат изпълнени, се определят от истинския тип
на обекта в паметта, чийто член се изпълнява
16. Предефиниране на членове
• Деклариране на виртуални членове
– Ключова дума virtual преди декларацията на члена
(след модификатора за достъп)
class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("...");
}
}
17. Предефиниране на членове
• Деклариране на предефинирани членове
– Ключова дума override преди декларацията на члена
(след модификатора за достъп)
– В родителски клас нагоре по йерархията трябва да
бъде деклариран виртуален член със същите
наименование, тип и параметри и видимост
– Не е задъжлително всички виртуални членове да
бъдат предефинирани във всеки наследен клас
18. Предефиниране на членове
class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof woof!");
}
}
class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Meow.");
}
}
class Fish : Animal
{
}
19. Предефиниране на членове
• Използване на виртуални и предефинирани
членове
– Синтактично не се различава от използването на
невиртуални членове
– Поведението може да бъде различно
Animal animal = new Cat();
animal.MakeSound();
20. Предефиниране на членове
• Преизползване на родителския член
– Ключовата дума base, следвана от оператора . и
наименованието на члена (заедно с аргументи, ако са
нужни)
– Възможно е да бъде достъпен и друг член на
родителския клас по същия начин
21. Предефиниране на членове
class Book
{
public string Name { get; set; }
public string Author { get; set; }
public virtual void ReadFromConsole()
{
Console.Write("Input name: ");
Name = Console.ReadLine();
Console.Write("Input author: ");
Author = Console.ReadLine();
}
}
class ScientificBook : Book
{
public string ScientificField { get; set; }
public override void ReadFromConsole()
{
base.ReadFromConsole();
Console.Write("Input scientific field: ");
ScientificField = Console.ReadLine();
}
}
22. Предефиниране на членове
• Методът ToString()
– Виртуален метод на класа object
– Връща представяне на обекта в символен низ
– Използва се от string.Format(...) и
Console.Write(...)
– Реализацията в класа object връща пълното име на
типа данни на обекта
– Може да бъде предефиниран във всеки клас, за да
връща смислен низ
23. Предефиниране на членове
class Book
{
public string Name { get; set; }
public string Author { get; set; }
public override string ToString()
{
return string.Format("{0} by {1}", Name, Author);
}
}
24. Предефиниране на членове
• Приложение на предефинирането на членове
– Често се налага една и съща „формална“ операция да
се реализира по различен начин вътрешно в
различни класове
– Гъвкавост на вътрешната реализация
– Разширяемост на членовете на родителските класове
– Избягване на превръщания надолу по йерархията
(downcasting)
25. Абстрактни класове и членове
• Какво е „абстрактен клас“?
– Не може да бъде инстанциран директно
– Служи за деклариране на членове, които са общи за
класовете от йерархията
– Не се ангажира с пълната реализация на поведението
им
– Използва се като събирателен тип данни в
променливи, параметри и изрази
26. Абстрактни класове и членове
• Деклариране на абстрактни класове
– Ключова дума abstract преди декларацията на класа
(но след модификатора за достъп)
– Разрешено е декларирането на абстрактни членове в
тялото на класа
abstract class Animal
{
}
27. Абстрактни класове и членове
• Какво е „абстрактен член“?
– Виртуален член на клас
– Няма декларирана реализация
– Класът задължително трябва да бъде маркиран като
абстрактен
– Ако наследен клас не предефинира всички абстрактни
членове, той също трябва да бъде маркиран като
абстрактен
28. Абстрактни класове и членове
• Деклариране на абстрактни членове
– Ключова дума abstract преди декларацията (но след
модификатора за достъп)
– Блоковете с операции се заместват с точка и запетая
– Въпреки че всички абстрактни членове са
виртуални, не се използва ключовата дума virtual
29. Абстрактни класове и членове
abstract class Animal
{
public abstract void MakeSound();
}
class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof woof!");
}
}
class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Meow.");
}
}
class Fish : Animal
{
public override void MakeSound()
{
throw new NotSupportedException("Fish don't make sounds.");
}
}
30. Абстрактни класове и членове
• Използване на абстрактни класове
– Не могат да бъдат инстанцирани пряко
– Могат да бъдат използвани за типове данни на
променливи
Animal animal = new Cat();
animal.MakeSound();
31. Абстрактни класове и членове
• Приложение на абстрактните класове и членове
– Позволяват създаването на общи звена от йерархията,
които не могат да реализират по смислен начин част
от поведението си (или цялото си поведение)
– Предотвратяват инстанцирането на обекти от
междинни класове, които не са предвидени за
самостоятелна употреба
32. Интерфейси
• Какво е „интерфейс“?
Тип данни
Сходен с напълно абстрактен клас без полета
Декларира членове, но не описва реализацията им
Всички членове са с публична видимост
Може да наследява (разширява) един или повече
други интерфейси
– Всеки клас може да имплементира един или повече
интерфейси, като предостави реализации на
членовете им
–
–
–
–
–
33. Интерфейси
• Деклариране на интерфейси
– Сходно с декларирането а класове
– Ключова дума interface
– Наименование – идентификатор в Pascal case с
представка I
– Тяло – блок с декларации на членове
• Методи, свойства, индексатори
• Блоковете с реализацията им се заместват с точка и запетая
• Не се прилагат модификатори за достъп – всички членове са
с публична видимост
35. Интерфейси
• Между наименованието на интерфейса и тялото
му може да се постави двоеточие, следвано от
списък от наименованията на
интерфейсите, които наследява (разширява)
interface IPet : INamedObject
{
int Age { get; }
}
36. Интерфейси
• Имплементиране на интерфейси
– В декларацията на клас, между наименованието и
тялото му се изброяват наименованията на
интерфейсите, които класът имплементира
– В случай, че е указан родителски клас на
класа, наименованията на интерфейсите
задължително се изброяват след него
– За всички членове на имплементираните интерфейси
в тялото на класа трябва да се декларират членове
със съответните типове данни, наименования и
параметри и с публична видимост
– Казваме, че членовете на класа имплементират
членовете на интерфейсите
37. Интерфейси
• Имплементиране на интерфейси
– Възможно е членовете да бъдат виртуални или дори
абстрактни
– Възможно е членове на родителските класове
автоматично да имплементират членове на
интерфейсите, ако имат необходимите типове данни,
наименования, параметри и видимост
38. Интерфейси
abstract class Animal : IVocalizingObject
{
public int Age { get; set; }
public abstract void MakeSound();
}
class Dog : Animal, IPet
{
public string Name { get; set; }
public override void MakeSound()
{
Console.WriteLine("Woof woof!");
}
}
class Cat : Animal, IPet
{
public string Name { get; set; }
public override void MakeSound()
{
Console.WriteLine("Meow.");
}
}
39. Интерфейси
• Използване на интерфейси
– Не могат да бъдат инстанцирани пряко
– Могат да бъдат използвани за типове данни на
променливи, за да се достъпят членовете им
IVocalizingObject vocalizingObject = new Cat();
vocalizingObject.MakeSound();
40. Интерфейси
• Приложение на интерфейсите
– Описване на чист „договор“ между обект, който
реализира някакво поведение, и външния код, който
го използва
– Всеки клас може да наследи само един родителски
клас, но да имплементира много интерфейси
– Сложни йерархии от класове с повече от една групи
характеристики и функционалности, реализирани
чрез интерфейси
41. Интерфейси
• Интерфейсът System.IDisposable
– Служи за освобождаване на заети ресурси в момент,
определен от програмиста
– Garbage Collector унищожава свободните обекти в
неопределен момент във времето
– Единствен метод void Dispose()
FileStream stream = File.OpenRead(@"C:File.txt");
try
{
// Операции
}
finally
{
stream.Dispose();
}
42. Интерфейси
• Блокът using
– Служи за автоматично изпълнение на Dispose()
– Ключова дума using
– Заграден в скоби израз, който имплементира
System.IDisposable
– Блок от операции
using (FileStream stream = File.OpenRead(@"C:File.txt"))
{
// Операции
}
43. Интерфейси
• Интерфейсът System.Collections.IEnumerable
– Служи за последователно изброяване елементите на
колекция
– Конструкцията foreach може да се приложи върху
всички обекти, които го имплементират
IEnumerable enumerable = new int[10];
foreach (int item in enumerable)
{
// Операции
}
44. Шаблони
• Какво е „шаблонен тип“ (generic type)?
– Клас, структура или интерфейс
– При декларирането му са посочени един или повече
типови параметри
– Типовите параметри заместват реални типове в
декларацията на типа и членовете му
– Не може да се използва пряко като тип данни
– Създават „шаблонни инстанции“, като типовите
параметри се заместят с истински типове данни
45. Шаблони
• Деклариране на шаблонни типове
– След наименованието на типа в ъглови скоби се
поставя списък от типови параметри
– Типов параметър е идентификатор; по конвенция или
еднобуквен – например T, U, V – или записан в Pascal
case с представка T
– Типовите параметри могат да се поставят вместо
истински типове данни в декларацията на типа
46. Шаблони
class Expression<T>
{
public T GetValue()
{
// Операции
}
}
class BinaryTreeNode<T>
{
public T Value { get; set; }
public BinaryTreeNode<T> Left { get; set; }
public BinaryTreeNode<T> Right { get; set; }
}
47. Шаблони
• Използване на шаблонни типове
– От шаблонните типове се създават шаблонни
инстанции, като след наимнованието им се поставя в
ъглови скоби списък от типове данни, които да
заместят типовите им параметри
BinaryTreeNode<int> node = new BinaryTreeNode<int>();
node.Value = 3;
48. Шаблони
• Използване на шаблонни типове
– Възможно е влагане на шаблонни типове данни
BinaryTreeNode<Expression<bool>> expressionNode =
new BinaryTreeNode<Expression<bool>>();
49. Шаблони
• Ограничения на типовите параметри
– Налагат ограничения върху допустимите типове
данни, които да бъдат използвани за създаването на
шаблонна инстанция
– Позволяват използването на съответните за
ограничението членове или оператори върху данните
от този тип
– Поставят се непосредствено преди тялото на типа
– Ключова дума where
– Типов параметър
– Двоеточие
– Списък с ограничения
50. Шаблони
• Ограничения на типовите параметри
Да е референтен тип данни – ключова дума class
Да не е референтен тип данни – ключова дума struct
Да има конструктор без параметри – new()
Да наследява родителски клас – наименование на
родителския клас
– Да имплементира интерфейс – наименование на
интерфейса
– Да наследява или имплементира друг типов
параметър – наименование на типовия параметър
–
–
–
–
51. Шаблони
class BinaryTreeNode<T>
where T : class
{
public T Value { get; set; }
public BinaryTreeNode<T> Left { get; set; }
public BinaryTreeNode<T> Right { get; set; }
}
52. Шаблони
• Приложения на шаблонните типове
– Строго типизирани структури от данни, които се
ползват за различни типове на съхраняваните данни;
например речници, дървета, стекове, опашки,
списъци, множества и т.н.
– Строго типизирани алгоритми, които работят за
различни типове на данните; например сортиране,
отсяване на елементи, обработка на данни
53. Шаблони
• Какво е „шаблонен метод“ (generic method)?
– Сходен с шаблонен тип
– При декларирането му са посочени един или повече
типови параметри
– Типовите параметри заместват истински типове данни
в декларацията му
– При изпълнението му се указват конкретни типове
данни, с които да бъдат заместени типовите
параметри
– Възможно е да бъдат наложени ограничения на
типовите параметри
54. Шаблони
• Деклариране на шаблонни методи
– След наименованието на метода в ъглови скоби се
поставя списък от типови параметри
– Между списъка от параметри на метода и тялото му
могат да бъдат поставени ограничения на типовите
параметри
public static void BubbleSort<T>(T[] array)
where T : IComparable
{
// Операции
}
55. Шаблони
• Използване на шаблонни методи
– Между наименованието и списъка с аргументи на
метода в ъглови скоби се поставят типове данни, с
които да бъдат заместени типовите параметри
int[] numbers = new int[10];
BubbleSort<int>(numbers);
– Възможно е ъгловите скоби да се пропуснат, ако
компилаторът може да определи типовете, с които са
заместени типовите параметри автоматично
int[] numbers = new int[10];
BubbleSort(numbers);
56. Шаблони
• Приложение на шаблонните методи
– Реализиране на строго типизирани алгоритми, които
работят за различни типове на данните; например
сортиране, отсяване на елементи, обработка на данни