Российский Государственный Технологический Университет им. К.Э. Циолковского
Опубликован: 02.03.2007 | Доступ: свободный | Студентов: 5684 / 804 | Оценка: 3.96 / 3.45 | Длительность: 27:04:00
ISBN: 978-5-9556-0086-4
Лекция 13:

Шаблоны

Общее представление

В C# можно объявлять шаблоны классов и шаблоны функций. Шаблоны классов и шаблоны функций – две разные вещи, которые в общем случае могут сосуществовать в рамках одного и того же объявления.

Объявление любого шаблона предполагает использование специальных обозначений, которые называются параметрами шаблона. Традиционно для этого применяются однобуквенные обозначения: A, ..., K, L, M, N, ..., T, ..., Z.

При объявлении шаблонного класса или функции параметры шаблона заменяются на конкретные имена классов.

Шаблонный класс – это класс, который строится на основе ранее объявленного шаблона для определенного класса (этот класс мы будем называть подстановочным классом). Шаблонный классфункция) обеспечивают стандартную реализацию какой-либо функциональности для подстановочного класса — например, построения специализированной коллекции для множества объектов данного типа.

Параметры шаблонов позволяют параметризовать шаблоны классов, структур, интерфейсов, делегатов и функций классами, для которых эти шаблоны создавались с целью расширения их функциональности.

Ниже приводится пример объявления шаблона класса Stack с параметром типа <T>.

Параметр T в объявлении рассматриваемого шаблонного класса обозначает место подстановки реального типа. Он используется для обозначения типа элемента для внутреннего массива элементов, типа параметра метода Push и типа результата для метода Pop:

public class Stack<T>
 {
 T[] items;
 int count;
 public void Push(T item) {...}
 public T Pop() {...}
 }

Вместо преобразований в и из класса object, параметризованные коллекции ( Stack<T> в том числе) принимают на сохранение значения типа, для которого они созданы, и сохраняют данные этого типа без дополнительных преобразований. При использовании шаблонного класса Stack<T>, вместо T подставляется реальный тип. В следующем примере int задается как аргумент типа (type argument) для T:

Stack<int> stack = new Stack<int>();
 stack.Push(3);
 int x = stack.Pop();

Тип Stack<int> называют составным или шаблонным типом (constructed type). При объявлении этого типа каждое вхождение T заменяется аргументом типа int.

Когда объект – представитель класса Stack<int> создается, массив items оказывается объектом типа int[], а не object[].

Методы Push и Pop класса Stack<int> также оперируют значениями int, при этом помещение в стек значений другого типа приводит к ошибкам компиляции (а не ошибкам времени выполнения).

Также не требуется явного приведения извлеченных из коллекции значений к их исходному типу.

Шаблоны обеспечивают строгий контроль типов. Это означает, например, что помещение int в стек объектов XXX является ошибкой.

Точно так же, как Stack<int> ограничен возможностью работы только со значениями типа int, так и Stack<XXX> ограничен объектами типа XXX.

А компиляция последних двух строк следующего примера выдаст ошибку:

Stack<XXX> stack = new Stack<XXX>();
 stack.Push(new XXX());
 XXX xxx = stack.Pop();
 stack.Push(3);		// Ошибка несоответствия типов
 int x = stack.Pop();	// Ошибка несоответствия типов

Объявление шаблона может содержать любое количество параметров типа. В приведенном выше примере в Stack<T> есть только один параметр типа, но шаблонный класс Dictionary должен иметь два параметра типа: один для подстановочного типа ключей, другой для подстановочного типа значений:

public class Dictionary<K,V>
 {
 public void Add(K key, V value) {...}
 public V this[K key] {...}
 }

При объявлении шаблонного класса на основе шаблона Dictionary<K,V> должны быть заданы два аргумента типа:

Dictionary<string,XXX> dict = new Dictionary<string,XXX>();

 // В словарь добавляется объект типа XXX,
 // проиндексированный строкой x1
dict.Add("x1", new XXX()); 

 // Извлечение из словаря элемента, проиндексированного строкой "x1"
 XXX xxx = dict["x1"];

Пример использования шаблонов: сортировка.

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

using System;
 using System.Collections;
 using System.Collections.Generic;

namespace PatternArrays
 {

 // Данные для массива элементов.
 // Подлежат сортировке в составе шаблонного массива методом Sort.
class Points
 {
 public int x;
 public int y;

public Points(int key1, int key2)
 {
     x = key1;
     y = key2;
 }

 // Вычисляется расстояние от начала координат.
 public int R
 {
     get
    {
         return (int)(Math.Sqrt(x * x + y * y));
     }
 }
 }


 // ...ШАБЛОННЫЙ КОМПАРЕР на основе шаблона интерфейса...   


class myComparer : IComparer<Points>
 {

 // Предлагаемый ШАБЛОННЫЙ метод сравнения возвращает разность расстояний
 // двух точек (вычисляется по теореме Пифагора) от начала координат
 // –  точки с координатами (0,0). Чем ближе точки к началу координат
 // –  тем они меньше. Не требуется никаких явных приведений 
типа.
 // Шаблон настроен на работу с классом Points.


int IComparer<Points>.Compare(Points p1, Points p2)
 {
     return (p1.R - p2.R);
 }
 }
 // После реализации соответствующего ШАБЛОННОГО интерфейса объект-
 // КОМПАРЕР обеспечивает реализацию стандартного алгоритма сортировки.

class Class1
 {
 static void Main(string[] args)
 {
     // Объект - генератор "случайных" чисел. 
    Random rnd = new Random();

    int i;
     // Очередь Points.
     Queue<Points> qP = new Queue<Points>();
     // Шаблонный перечислитель. Предназначен для обслуживания
     // шаблонной очереди элементов класса Points.
     IEnumerator<Points> enP;
     // Сортировка поддерживается классом Array.
     Points[] pp;

    // Создали Компарер, способный сравнивать пары
     // объектов - представителей класса Points.  
    myComparer c = new myComparer();

    Console.WriteLine("========================================");

    // Проинициализировали массив объектов - представителей класса Points. 
    for (i = 0; i < 10; i++)
     {
         qP.Enqueue(new Points(rnd.Next(0, 10), rnd.Next(0, 10)));
     }

    enP = ((IEnumerable<Points>)(qP)).GetEnumerator();
     for (i = 0; enP.MoveNext(); i++)
     {
         Console.WriteLine("{0}: {1},{2}", i, enP.Current.x, enP.Current.y);
     }

    // Сортируются элементы массива типа Points, который формируется на 
   // основе шаблонной очереди.
   // Условием успешной сортировки элементов массива является реализация
   // интерфейса IComparer. Если Компарер не сумеет справиться с
   // поставленной задачей – будет возбуждено исключение.
   // На основе очереди построили массив. 
    pp = qP.ToArray();

    // А саму очередь можно почистить!
     qP.Clear();

    try
    {
         Array.Sort<Points>(pp, c);
     }
     catch (Exception ex)
     {
         Console.WriteLine(ex);
     }

    // Сортировка произведена, очередь восстановлена.
     for (i = 0; i < 10; i++) qP.Enqueue(pp[i]);

    Console.WriteLine("========================================");

    enP = ((IEnumerable<Points>)(qP)).GetEnumerator();
     for (i = 0; enP.MoveNext(); i++)
     {
         Console.WriteLine("{0}: {1},{2}", i, enP.Current.x, enP.Current.y);
     }

    Console.WriteLine("========================================");
 }
 }
 }
Листинг 13.1.
kewezok kewezok
kewezok kewezok
Елена Шляхт
Елена Шляхт
Объясните плиз в чем отличие а++ от ++а
Почему результат разный?
int a=0, b=0;
Console.WriteLine(a++); //0
Console.WriteLine(++b); //1
a++;
++b;
Console.WriteLine(a); //2
Console.WriteLine(b); //2